title: Angular
tags:
- Angular
- JS
Angular
简介
- 一款非常优秀的前端高级 JS 框架
- 最早由Misko Hevery等人创建
- 09被Google收购
- 可以轻松构建 SPA 应用程序
- 其核心就是通过指令扩展HTML,通过表达式绑定数据到HTML
SPA
- single page application 单页应用程序
//根据url中锚点值的变化,动态的请求不同的数据
<a href="#find"></a>
window.addEventListener("hashchange",function(){
//hashchange监听url中锚点值变化的事件
var hash= location.hash;
//location.hash, location.href
switch(hash){
case '#find':
$.get('find.json',function(data){
console.log(data);
},'json');
break;
case:...;
}
})
指令
- 指令:在angular中以ng-开头的html标签属性
- ng-app: 选择angular去管理哪一部分的html代码, 管理的是ng-app所在元素的子元素及其本身
- ng-model: 指定一个属性值,这个属性就表示当前的value值,只能用在input中和select中
- ng-init: 可以对数据进行初始化操作,给一个默认值
- ng-click: 注册点击事件
<!-- 约定ng-app -->
<body ng-app>
<!-- 获取值ng-model -->
<input type="text" ng-model="val">
<!-- 添加点击事件ng-click -->
<input type="text" value="+1" ng-click="myVal-0+1">
<div>{{val}}</div>
<!-- 初始化ng-init -->
<div ng-init="val=1"></div>
</body>
<!-- 引包 -->
<script src="./libs/angular.js"></script>
模块
- 创建模块
var app = angular.module('模块名',[])
- 如果不依赖其他模块,也需要提供一个空的数组
- 需要在ng-app指令的属性值上写我们的模块名
- 模块的划分方式
- 按照项目的功能去划分模块
- 按照项目中文件的类型去划分模块
控制器
- 创建控制器
app.controller('控制器的名字',function($scope){})
- 要在它所在标签或者父标签上加上ng-controller指令,ng-controller的值就是我们想要显示的数据模型所在控制器的名字
双向数据绑定
- 数据模型($scope.属性)的改变,会影响内容的显示(文本框的值)
- 改变文本框的值,对应的数据模型发生了改变
<body ng-app="hello">
<input type="text" ng-model="name">
<span>{{name}}</span>
<input type="button" value="打印值" ng-click="getName()">
<input type="button" value="改变为小红" ng-click="setName()">
<script src="./libs/angular.js"></script>
<script>
// 创建模块
var app=angular.module('hello',[]);
// 创建控制器
app.controller('helloController',function($scope){
// 初始化数据模型
$scope.name="小明";
// 初始化行为模型
$scope.getName=function(){
console.log($scope.name)
};
$scope.setName=function(){
$scope.name="小红"
}
})
</script>
</body>
MVC 思想
- M : Model: 存储,获取数据
- V : View 视图,把数据呈现给用户
- C : Controller 做一些控制和调度的操作
- MVC只是一种思想,只是约定了我们代码应该如何去组织,让我们代码的每一部分都有一个明确的职责,用利于后期的维护性
<body ng-app="app">
<!-- view 开始 -->
<table ng-controller="demoController">
<tr><td>用户名: <input type="text" ng-model="username"></td></tr>
<tr><td>密码: <input type="text" ng-model="userpwd"></td></tr>
<tr><td>用户类型:
<select ng-model="type">
<option value="tmp01">学生</option>
<option value="tmp02">老师</option>
<option value="tmp03">校长</option>
</select>
</td></tr>
<tr><td>注册协议: <input type="checkbox" ng-model="isAgree"></td></tr>
<tr><td><input type="button" value="注册" ng-click="register()"></td></tr>
<tr><td>{{msg}}</td></tr>
</table>
<!-- View 结束 -->
<script src="libs/angular.js"></script>
<script>
var app = angular.module('app',[])
// Controller 开始
app.controller('demoController',function($scope){
// 初化数据模型
$scope.username = '';
$scope.usertype = '';
$scope.userpwd = '';
$scope.isagree = false;
$scope.msg = ''
// 初始化行为模型
$scope.register = function(){
if($scope.userpwd.length<6){
$scope.msg='密码太短,不安全!';
return
}
// 准备存储数据
// 调用用Model里的方法
var u = new User($scope.username,$scope.userpwd,$scope.type);
var result = u.save();
if(result){
$scope.msg = '注册成功!'
}else{
$scope.msg = '注册失败!'
}
}
})
// Controller 结束
// Model 开始
function User(name,pwd,type){
this.name = name;
this.pwd = pwd;
this.type = type
}
User.prototype.save = function(){
// 判断用户名是否存在
var str = localStorage.getItem('myusers')||'[]';
// 注意'[]'
var arr = JSON.parse(str);
for (var i = 0; i < arr.length; i++) {
if(arr[i].name===this.name){
return
}
}
arr.push({name:this.name,pwd:this.pwd,type:this.type});
localStorage.setItem('myusers',JSON.stringify(arr));
return true
}
</script>
</body>
Angular开发流程回顾
- 1.引包:引入angular.js文件
- 2.约定:加上ng-app指令,告诉angular要管理页面哪一部分代码
- 3.创建模块:在js中创建模块,给页面中的ng-app指令一个值,这个值就是这个模块的模块名
- 4.创建控制器:在js中创建控制器,需要在页面上加上ng-controller指令,指令的值为控制器的名字
- 5.建模,(数据模型,行为模型)根据页面结构,抽象出具体的js对象.
- 6.初始化,通过$scope做一些初始化操作
- 7.通过
ng-model , ng-click , 插值表达式
把$scope的属性在页面展示出来 - 8.在js写一些具体的逻辑
augular.element (不推崇)
- (jqLite对象) 类似于jq,但是要求传入的参数是一个原生的dom对象,而不是选择器
$scope
- 视图和控制器之间的数据桥梁,用于在视图和控制器之间传递数据,用来暴露数据模型(数据,行为)
正是因为$scope在Angular中大量使用甚至盖过了C(控制器)的概念,所以很多人把Angular称之为MVVM框架,$scope 实际上就是MVVM中所谓的VM(视图模型)
控制器
传统的方式创建控制器
// 创建控制器(1.2.x版本)
// angular会把全局的函数当作控制器
function demoController($scope){
$scope.name = '小明'
}
面向对象的方式创建控制器
<!-- 这里的obj 代表控制器中回调函数new 出的对象 -->
<div ng-controller="demoController as obj">
<p>{{myname}}</p>
<p>{{obj.name}}</p>
</div>
var app = angular.module('myApp', [])
// angular会把这二个参数当作构造函数使用
app.controller('demoController', function($scope){
$scope.myname='小红'
this.name = '小明'
})
安全的方式创建控制器
- 为了避免压缩后代码无法运行
// 把第二个参数改为一个数组,在数组把我们需要的参数的名字写上
// 回调函数就写在数组的最后一个元素上
// 数据中传入的元素的顺序,要和function的中顺序一一对应
app.controller('demoController',['$scope','$log',function($scope,$log){
$scope.msg = 'hello World!'
$log.log('哈哈哈哈!')
//注意$log.log()
}])
// 注意是'$scope'
指令
ng-bind
<p ng-bind="msg"></p>
解决表达式闪烁问题,浏览器不会把标签的属性显示出来,angular会把ng-bind对应的数据显示到所在标签中间
ng-cloak
<div class="ng-cloak">{{msg}}</div>
也可以解决表达闪烁问题,注意ng-cloak是样式,我们要先给ng-cloak设置display:none,angular在解析表达式之后会把页面上的ng-cloak样式移除,这样ng-cloak对应的样式就不生效了
ng-repeat
- 能够把一组数据直接渲染到页面上,不需要我们拼接字符串
- ng-repeat="item in users",item对应是遍历users时的每一条数据,users是我们要遍历的数据(是一个数组)
<body ng-app="myApp">
<div ng-controller="demoController">
<ul>
<li ng-repeat="item in users" >
{{item.name}} {{item.age}}
</li>
</ul>
<ul>
<li ng-repeat="item in obj">
{{item.name}} {{item.age}}
</li>
</ul>
<ul>
<li ng-repeat="item in arr track by $index">
{{item}} {{$index}}
</li>
</ul>
</div>
<script src="libs/angular.js"></script>
<script>
var app = angular.module('myApp', [])
app.controller('demoController',['$scope',function($scope){
$scope.users = [
{name:'小明',age:18},
{name:'小红',age:18},
{name:'小朋',age:28},
{name:'小月',age:19},
{name:'小黑',age:18},
{name:'小白',age:20}
]
$scope.obj = {
xm:{name:'小明',age:18},
xh:{name:'小红',age:28},
xt:{name:'小天',age:10},
xy:{name:'小月',age:38}
}
$scope.arr = [1,2,3,4,5,1]
}])
// 注意:item in arr track by $index
</script>
</body>
- ng-repeat 在遍历时会提供以下值:
- $odd : 为true时,表明当前数据是否是第[奇]数条
- $even: 为true时,表明当前数据是否是第[偶]数条
- $first: 为true时,表明当前数据是否是第1条
- $last: 为true时,表明当前数据是否是最后一条
- $middle: 为true时,表明当前数据是否是中间的数据
<li class="{{ $odd ?'red':'green'}}" ng-repeat="item in data">
{{item.name}},{{item.age}}
</li>
ng-class
- 动态的添加class样式,以对象的形式书写,angular会把属性值为true的属性名当作样式添加到class
<!-- ng-class,动态的添加class样式,
以对象的形式书写,angular会把属性值为true的属性名当作样式添加到class-->
<li ng-class="{red:item.age>=20, green:item.age>=10&&item.age<20,blue:item.age<10}" ng-repeat="item in data">
{{item.name}},{{item.age}}
</li>
<!-- 注意格式ng-class="{classname:true,classname2:true,...}" -->
ng-show/ng-hide
- ng-show,控制元素的显示或隐藏,值为true时显示,为false隐藏,ng-hide作用是相反的(只是设置display:none来隐藏元素)
<body ng-app="myApp">
<div ng-controller="demoController">
<p ng-show="isShowing">hello</p>
<button ng-click="showOrHide()">显示/隐藏</button>
</div>
<script src="libs/angular.js"></script>
<script>
var app = angular.module('myApp', [])
app.controller('demoController', ['$scope', function($scope){
$scope.isShowing= true;
$scope.showOrHide=function(){
$scope.isShowing = !$scope.isShowing
}
}])
</script>
</body>
ng-if
- 与ng-show用法一致,注意当值为false时,会将当前dom元素移除
ng-switch
- 当ng-switch-when对应的值等于ng-switch对应值时,当前dom元素就显示
<body ng-app="myApp">
<div ng-controller="demoController">
<div ng-switch="name">
<div ng-switch-when="小明">
我是小明
</div>
</div>
</div>
<script src="libs/angular.js"></script>
<script>
var app = angular.module('myApp', [])
app.controller('demoController',['$scope', function($scope){
$scope.name = '小明'
}])
// 注意ng-switch-when
</script>
其他常用指令
- ng-model:双向数据绑定
- ng-checked:单选/复选是否选中(单向数据绑定,界面操作不会影响数据模型的值)
- ng-selected:是否选中(单向数据绑定)
- ng-disabled:是否禁用
- ng-readonly:是否只读
常用事件指令
- ng-blur:失去焦点
- ng-focus:获得焦点
- ng-change:内容改变
- ng-copy:复制
- ng-click: 单击
- ng-dblclick:双击
- ng-submit:form表单提单
指令的其他使用方式
- 兼容H5,在使用angular指令时,只需要在原先的指令前加上data-或x-,如:data-ng-app,x-ng-click
todomvc案例
todomvc功能分析
- 任务的展示
- 添加任务
- 删除任务
- 修改任务内容
- 切换任务完成与否的状态
- 批量切换任务完成与否的状态
- 显示未完成的任务数
- 清除所有已完成任务
- 注意: 在循环删除数组长度,会导致循环条件改变,也会导致元素原来的索引改变
- 切换显示不同状态的任务
过滤器(filter)
- 格式化数据
- 过滤数据(filter)
- currency,date
<!-- 在数据模型后加上| 再加上过滤器的名字
也可以在过滤器名字后指定参数,参数是写在冒号后面的-->
<p>{{money | currency : '¥'}}</p>
<p>{{myDate | date : 'yyyy年MM月dd日 HH:mm:ss'}}</p>
- lowercase 转换成小写,uppercase 转换成大写,number 四舍五入
- limitTo
<!-- limitTo第一个参数,表明显示多少个字,第二个参数表示,从第几个字开始显示(索引从0开始) -->
<p>{{msg | limitTo : 5 : 2}}...</p>
- orderBy 及 json
<!-- json格式化显示json数据,参数指定缩近的长度 -->
<pre>{{myJson | json : 8}}</pre>
<!-- orderBy对数据进行排序,参数,给+号就按正序排,- 就按倒序排 -->
<span ng-repeat="item in arr | orderBy:'-'">{{item}},</span>
- 在js中使用过滤器
app.controller('filterController', ['$scope','$filter',function($scope,$filter){// $filter 需要在控制器的回调中传入
$scope.money = 9998;
// 可以调用不同的过滤器得到相应的结果
// 参数是一个过滤器的名字
// 返回值是一个方法
// 后面括号里的第一个参数是需要处理的数据
// 后面括号里的的第二个参数是当前过滤器本身需要的参数
// $filter('过滤器名称')(需要过滤的数据,filter 参数)
$scope.result = $filter('currency')($scope.money,'¥')
}])
- filter
数据模型 | filter : {key:value}
<body ng-app="filterApp" ng-controller="fitlerController">
<div>
<input type="text" ng-model="search">
<ul>
<li ng-repeat="item in todos | filter : {completed:true} ">
{{item.name}},{{item.completed}}
</li>
<!-- <li ng-repeat="item in todos | filter : search ">
{{item.name}},{{item.completed}}
</li> -->
</ul>
<!-- 如果指定一个布尔值,或者字符串就是全文匹配 -->
<!-- 数据精确过滤 参数:1、scope的属性,2、自定的字符串 3、自定的对象 {}-->
<!-- 会到对应的todos中寻找,如果当前元素有completed属性且值 为true就会被显示出来。(只会到completed属性中寻找) -->
</div>
<script src="libs/angular.js"></script>
<script>
var app = angular.module('filterApp', [])
app.controller('fitlerController', ['$scope',function($scope){
$scope.search='';
$scope.todos = [
{id:1,name:'吃饭',completed:true},
{id:2,name:'睡觉吃饭',completed:true},
{id:3,name:'打豆豆',completed:false},
{id:4,name:'学习', completed:true},
{id:5,name:'喝水,true',completed:false}
]
}])
</script>
</body>
$watch 监视数据模型的变化
<body ng-app="hello">
<div ng-controller="helloController">
<input type="text" ng-model="name">
<input type="text" ng-model="age">
<button>测试</button>
</div>
<script src="libs/angular.js"></script>
<script>
var app = angular.module('hello', [])
app.controller('helloController',['$scope',function($scope){
$scope.name = '小明'
$scope.age = 18
// $watch可以用来监视数据模型的变化
// 第一个参数: 数据模型对应的名字(字符串形式)
// 第二个参数: 相应的数据模型变化就会调用这个函数
// 默认会直接执行一次回调函数
$scope.$watch('name',function(now,old){
// 第一个参数是变化后的值
// 第二个参数是变化前的值
console.log(now,old)
})
$scope.getAge = function(){
return $scope.age
}
// 也能够监视$scope.属性中的方法的返回值
$scope.$watch('getAge()',function(now,old){
console.log(now,old)
})
// $watch监视的是$scope的属性,如果是一个普通变量是无法监视的
// function getName(){
// return $scope.name
// }
// $scope.tmp = getName
// $scope.$watch('tmp()',function(now,old){
// console.log(now,old)
// })
}])
</script>
</body>
自定义属性
<body ng-app="directiveApp">
<!-- 以属性形式使用 -->
<!-- <div my-btn></div> -->
<!-- 以样式名形式使用 -->
<!-- <div class="my-btn"></div> -->
<!-- 以自定义标签形式使用 -->
<my-btn mystyle="red">咦</my-btn>
<script id='tpl' type='text/ng-template'>
<div>
<!-- <p class="tmp"> 我是在模板里定义的p,没有写ng-transclude</p> -->
<p class="{{mystyle}}">hello</p>
<p ng-transclude>hey</p>
</div>
</script>
<script src="libs/angular.js"></script>
<script>
var app = angular.module('directiveApp', [])
// 创建自定义指令 directive
// 第一个参数:是指令的名字,必须是驼峰命名法,使用时把大写改成小写,在原来大写前加上-
// 第二个参数:和控制器的第二个参数类似
app.directive('myBtn', [function(){
// 在这里直接return 一个对象就可以了
return {
// template:'hello'
// templateUrl:'template.html'
templateUrl:'tpl',
restrict:'ACE',
replace:true,
transclude: true,
scope:{
// tmp:'@mystyle',
mystyle:'@',
},
link:function(scope,element,attributes){
scope.msg="hello"
console.log(element)
element.on('click',function(){
alert('你点我了!')
})
console.log(attributes)
}
}
}])
//注意directive,type='text/ng-template',transclude
</script>
</body>
自定义指令中回调里返回的对象的属性
template: 需要提供一个html字符串,会被添加到当前页面使用了自定义指令的位置
-
templateUrl:
- 需要提供一个html文件路径,angular会发请求,请求对应的文件,把文件内容作为模板插入到自定义指令中间
- 提供一个script标签的id, angular会把script标签中的内容作为模板插入到自定义指令中间,注意要改变script标签的type="text/ng-template"
-
restrict: 需要提供一个字符串,限制自定义指令的使用形式
- A : Attribute 表示只能以属性的形式使用
- C : Class 表示只能以类样式名的形式使用
- E : Element 表示只能以自定义标签的形式使用
- ACE : 如果希望多种方式合适,就把对应值组合在一起
replace:需要提供一个布尔值,为true时,模板会被用来替换自定义指令所在标签,否则是插入到自定义指令中间
transclude: 需要一个布尔值, 为true时会将自定义标签中的内容插入到模板中拥有ng-transclude指令的标签中间
-
scope:需要一个对象: 可以用来获取自定义指令的属性值,
- 给当前对象添加一个属性(如:tmp),属性值以@开头,后面跟上自定义指令的属性名
然后就可以在模板中使用{{tmp}} 来得到对应的属性值,注意''-
scope: { tmp:'@mystyle'}
{{tmp}}
-
scope: { mystyle:'@'}
{{mystyle}}
-
- 给当前对象添加一个属性(如:tmp),属性值以@开头,后面跟上自定义指令的属性名
-
link: 需要一个function 这是function在angular解析到相应指令时就会执行一次,
- scope :类似于$scope,只不过scope的属性 只能在模板中使用
- element : 自定义指令所在标签对应的对象(jqLite),指向模板最外层的对象,如果replace为treu,指向的就是自定义指令所在标签
- attributes : 包含了自定义指令所在标签的必有属性
服务
- 创建服务
var app = angular.module('service',[])
// 第一个参数:服务的名字
// 第二个参数里的function:
// angular会把这个function当作构建函数,angular会帮助我们new这个构建函数,然后得到一个对象
app.service('MyService', [function(){
this.name = '小明'
}])
- 使用服务
var app = angular.module('todosApp', ['service'])
app.controller('todosController',['MyService',function(MyService){
// 这个MyService就是,对应的'MyService'时的回调函数new出的对象
console.log(MyService)
}])
路由
- 根据url中不同的锚点值,在页面显示不同的内容
路由使用
<body ng-app="myApp">
<div ng-view></div>
<script src="libs/angular.js"></script>
<script src="libs/angular-route.js"></script>
<script>
var app=angular.module('myApp',['ngRoute'])
//注意['ngRoute']
// $location.url()可以得到url中锚点值#后的部分
// 配置路由规则
app.config(['$routeProvider',function($routeProvider){
// 第一个参数:对应的url中锚点值
// 当angular检测到url中锚点变为/a,就会把template对应的内容插入到页面拥有ng-view指令的标签中
$routeProvider
.when('/a',{
template:'<div>{{msg}}</div>',
controller:'demoController'
// 指定一个控制器的名字,
// 当前url中锚点值为/a时就会执行控制器中的回调函数
// 我们可以直接在template/templateUrl对应的模板中使用该控制器中对应的$scope属性值
})
.when('/b',{
template:'<div>hello</div>'
})
}])
app.controller('demoController',['$scope',function($scope){
$scope.msg="hi";
}])
</script>
</body>
路由参数
<body ng-app="myApp">
<div ng-view></div>
<script src="libs/angular.js"></script>
<script src="libs/angular-route.js"></script>
<script>
var app=angular.module('myApp',['ngRoute']);
app.config(['$routeProvider',function($routeProvider){
$routeProvider.when('/company/:myname?',{
//在原有规则中可以加冒号:,:号后跟上一个名字(相当于变量名,当前位置可以写成任意的值,但是必须要有) ?号表示当前位置如果没有值,也可以匹配到
template:'<div>{{msg}}</div>',
controller:'demoController'
})
.otherwise({redirectTo:'/company/a'})
// 当不匹配when方法对应的规则时,就会匹配otherwise对应的规则
}])
app.controller('demoController',['$scope','$routeParams',function($scope,$routeParams){
$scope.data={
a:'hello',
b:'hi',
c:'hey'
};
$scope.msg=$scope.data[$routeParams.myname]
// 在控制器中可以通过$routeParams得到对应的值
}])
</script>
</body>