我觉得angularjs是前端框架,而jquery只是前端工具,这两个还是没有可比性的。
看知乎上关于jquery和angular的对比http://www.zhihu.com/question/27471743
优点:1. 模板功能强大丰富,并且是声明式的,自带了丰富的Angular指令;2. 是一个比较完善的前端MV*框架,包含模板,数据双向绑定,路由,模块化,服务,过滤器,依赖注入等所有功能;3. 自定义Directive,比jQuery插件还灵活,但是需要深入了解Directive的一些特性,简单的封装容易,复杂一点官方没有提供详细的介绍文档,我们可以通过阅读源代码来找到某些我们需要的东西,如:在directive使用 $parse;4. ng模块化比较大胆的引入了Java的一些东西(依赖注入),能够很容易的写出可复用的代码,对于敏捷开发的团队来说非常有帮助,我们的项目从上线到目前,UI变化很大,在摸索中迭代产品,但是js的代码基本上很少改动。5. 补充:Angular支持单元测试和e2e-testing。缺点:1. 验证功能错误信息显示比较薄弱,需要写很多模板标签,没有jQuery Validate方便,所以我们自己封装了验证的错误信息提示,详细参考 why520crazy/w5c-validator-angular · GitHub ;2. ngView只能有一个,不能嵌套多个视图,虽然有 angular-ui/ui-router · GitHub 解决,但是貌似ui-router 对于URL的控制不是很灵活,必须是嵌套式的(也许我没有深入了解或者新版本有改进);3. 对于特别复杂的应用场景,貌似性能有点问题,特别是在Windows下使用chrome浏览器,不知道是内存泄漏了还是什么其他问题,没有找到好的解决方案,奇怪的是在IE10下反而很快,对此还在观察中;4. 这次从1.0.X升级到1.2.X,貌似有比较大的调整,没有完美兼容低版本,升级之后可能会导致一个兼容性的BUG,具体详细信息参考官方文档 AngularJS ,对应的中文版本:Angular 1.0到1.2 迁移指南5. ng提倡在控制器里面不要有操作DOM的代码,对于一些jQuery 插件的使用,如果想不破坏代码的整洁性,需要写一些directive去封装插件,但是现在有很多插件的版本已经支持Angular了,如:jQuery File Upload Demo6. Angular 太笨重了,没有让用户选择一个轻量级的版本,当然1.2.X后,Angular也在做一些更改,比如把route,animate等模块独立出去,让用户自己去选择。当然使用的人多才会暴露更多的问题,一起为这些问题寻找解决方案是一个社区的良性趋势,选择Angular,的确使我们的开发效率大大提高。
2009年google feedback project 1500行
为了解决ajax开发的痛苦
1. 几个常用的概念
a.客户端模板:模板和数据都会发送到浏览器中
b.mvc:
c.数据绑定:自动将Model和view间的数据同步。
angular 实现数据绑定的方式,可以让我们把model当做程序中唯一可信的数据来源。view始终是model的投影。。当model发生变化时,会自动反映到view上。
大多数的数据绑定都是单向的,当我们的model和view修改后要去提醒另外一个的话,就必须使用ajax之类的,比如jquery还要手动的同步,这样子的话,增加了不必要的麻烦。
d.依赖注入:
2.依赖注入 的详解
回顾下spring中的ioc 控制反转:
控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。 控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。依赖注入应用比较广泛。
个人理解:就是本来需要你在程序设计时,创建的某些对象,交给spring来管理,当你需要某个时候需要某个对象的时候spring在给你,这样子对象的生命周期之类的就由spring来维护了。
var A = function () {
this.getName = function () {
return '张三';
}
}
var B = function (obj) {
// B 依赖于 a
document.write(obj.getName());
}
// a 注入 ba
var a = new A;
var b = new B(a);
========================
方法2:
var A = function () {
this.getName = function () {
return '张三';
}
}
var a = new A;
var B = function () {
// B 依赖于 a
document.write(a.getName());
}
// a 注入 ba
var b = new B(a);
===========================
3. 在 ng-app下
<input type="text" ng-model="name" value=""/>
<!--angular的表达式-->
{{ name }}
<input type="text" ng-model="name" value=""/>
这三个name的数据是绑定。
4. 控制器
<div ng-controller="firstController">
<input type="text" value="" ng-model="name"/>
<input type="text" value="" ng-model="age"/>
{{name}}
{{age}}
</div>
var firstController = function($scope){
// $scope 我们叫做作用域
// 申明一个Model
$scope.name = '张三';
$scope.age = 20;
}
5. 多个控制器
<div ng-controller="firstController">
<input type="text" value="" ng-model="name"/>
<div ng-controller="secondController">
<input type="text" value="" ng-model="name"/>
</div>
</div>
var firstController = function($scope){
$scope.name = '张三';
console.log($scope);
}
var secondController = function($scope){
console.log($scope);
}
6. $apply方法
var firstController = function($scope){
$scope.date = new Date();
// setInterval(function(){
// // 这里虽然变 但是并没有触发 脏检查
// $scope.date = new Date();
//
// },1000)
setInterval(function(){
$scope.$apply(function(){
$scope.date = new Date();
//....会去触发脏检查
})
},1000)
// 触发一次脏检查
}
7. watch
var firstController = function($scope){
$scope.name = '张三';
$scope.data = {
name :'李四',
count:20
}
$scope.count = 0;
// 监听一个model 当一个model每次改变时 都会触发第2个函数
$scope.$watch('name',function(newValue,oldValue){
++$scope.count;
if($scope.count > 30){
$scope.name = '已经大于30次了';
}
});
$scope.$watch('data',function(){
},true)
// 上面的true表示只要有一个true发生改变,那么这个watch就触发。
}
8. 一个简单的互动的demo代码
1 <div ng-controller="cartController" class="container"> 2 <table class="table" ng-show="cart.length"> 3 <thead> 4 <tr> 5 <th>产品编号</th> 6 <th>产品名字</th> 7 <th>购买数量</th> 8 <th>产品单价</th> 9 <th>产品总价</th> 10 <th>操作</th> 11 </tr> 12 </thead> 13 <tbody> 14 <tr ng-repeat="item in cart"> 15 <td>{{item.id}}</td> 16 <td>{{item.name}}</td> 17 <td> 18 <button type="button" ng-click="reduce(item.id)" class="btn tn-primary">-</button> 19 <input type="text" value="{{item.quantity}}" ng-model="item.quantity" > 20 <button type="button" ng-click="add(item.id)" class="btn tn-primary">+</button> 21 </td> 22 <td>{{item.price}}</td> 23 <td>{{item.price * item.quantity}}</td> 24 <td> 25 <button type="button" ng-click="remove(item.id)" class="btn btn-danger">移除</button> 26 </td> 27 </tr> 28 <tr> 29 <td> 30 总购买价 31 </td> 32 <td> 33 {{totalPrice()}} 34 </td> 35 <td> 36 总购买数量 37 </td> 38 <td> 39 {{totalQuantity()}} 40 </td> 41 <td colspan="2"> 42 <button type="button" ng-click="cart = {}" class="btn btn-danger">清空购物车</button> 43 </td> 44 </tr> 45 </tbody> 46 </table> 47 48 <p ng-show="!cart.length">您的购物车为空</p> 49 </div>
1 var cartController = function ($scope) { 2 3 $scope.cart = [ 4 { 5 id: 1000, 6 name: 'iphone5s', 7 quantity: 3, 8 price: 4300 9 }, 10 { 11 id: 3300, 12 name: 'iphone5', 13 quantity: 30, 14 price: 3300 15 }, 16 { 17 id: 232, 18 name: 'imac', 19 quantity: 4, 20 price: 23000 21 }, 22 { 23 id: 1400, 24 name: 'ipad', 25 quantity: 5, 26 price: 6900 27 } 28 ]; 29 30 31 /** 32 * 计算购物总价 33 */ 34 $scope.totalPrice = function () { 35 var total = 0; 36 angular.forEach($scope.cart, function (item) { 37 total += item.quantity * item.price; 38 }) 39 return total; 40 } 41 42 /** 43 * 计算总购买数 44 */ 45 $scope.totalQuantity = function () { 46 var total = 0; 47 angular.forEach($scope.cart, function (item) { 48 total += parseInt(item.quantity); 49 }) 50 return total; 51 } 52 53 54 /** 55 * 找一个元素的索引 56 */ 57 var findIndex = function (id) { 58 var index = -1; 59 60 angular.forEach($scope.cart, function (item, key) { 61 if (item.id === id) { 62 index = key; 63 return; 64 } 65 }); 66 67 return index; 68 } 69 70 71 /** 72 * 为某个产品添加一个数量 73 */ 74 $scope.add = function (id) { 75 var index = findIndex(id); 76 77 if (index !== -1) { 78 ++$scope.cart[index].quantity; 79 } 80 } 81 82 83 /** 84 * 为某个产品减少一个数量 85 */ 86 $scope.reduce = function (id) { 87 var index = findIndex(id); 88 89 if (index !== -1) { 90 var item = $scope.cart[index]; 91 if(item.quantity > 1){ 92 --item.quantity; 93 }else{ 94 var returnKey = confirm('是否从购物车内删除该产品!'); 95 if(returnKey){ 96 $scope.remove(id); 97 } 98 } 99 100 } 101 } 102 103 /** 104 * 移除一项 105 */ 106 $scope.remove = function (id) { 107 108 109 var index = findIndex(id); 110 // 如果找到了那个item 111 if (index !== -1) { 112 $scope.cart.splice(index, 1); 113 } 114 115 // 自动做脏检查 116 } 117 118 // 监听数量 如果小于 1 则让用户判断是否要删除产品 119 $scope.$watch('cart',function(newValue,oldValue){ 120 121 angular.forEach(newValue,function(item,key){ 122 if(item.quantity < 1){ 123 var returnKey = confirm('是否从购物车内删除该产品!'); 124 if(returnKey){ 125 $scope.remove(item.id); 126 }else{ 127 item.quantity = oldValue[key].quantity; 128 } 129 } 130 }) 131 },true);
//如果这个方式和上边的reduce同时执行的话,特别是reduce为0的时候, 先执行reduce方法,remove之后,执行这个函数的时候,就已经找不到那个cart中对应的值了,所以confirm不会重复弹出。 132 133 134 135 }
9. 控制器属于模块 。
模块是组织业务的一个框架,在一个模块当中定义多个服务。当引入了一个模块的时候,就可以使用这个模块提供的一种或多种服务了。
angularjs本身的一个默认模块叫做ng,它提供了$http,$scope等等服务。
服务只是模块提供的多种机制中的一种,其他的而还有指令(directive),
过滤器(filter),及其他配置信心。
也可以在已有的模块中新定义一个服务,也可以先定义一个模块,然后在新模块中定义新服务,。
服务是需要显示的声明依赖引入关系的,让ng自动地做注入。
<div ng-app="myApp"> <div ng-controller="firstController"> {{name}} </div> </div>
var myApp = angular.module('myApp',[]); myApp.controller('firstController',function($scope){ $scope.name = '张三'; });
10.
$provide.service() 必须返回对象(数组也是对象),不能返回字符串之类的。
$provide.factory 这个可以返回任何类型。
var myApp = angular.module('myApp',[],function($provide){ // 自定义服务 $provide.provider('CustomService',function(){ this.$get = function(){ return { message : 'CustomService Message' } } }); // 自定义工厂 $provide.factory('CustomFactory',function(){ return [1,2,3,4,5,6,7]; }); // 自定义服务 $provide.service('CustomService2',function(){ return 'aaa'; }) }); myApp.controller('firstController',function($scope,CustomFactory,CustomService2){ $scope.name = '张三'; console.log(CustomFactory); console.log(CustomService2); }); //myApp.service(); //myApp.factory();
11.
服务本身是一个任意的对象。
ng提供服务的过程涉及它的依赖注入机制。
angular 是用$provider对象来实现自动依赖注入机制,注入机制通过调用一个provider的$get方法,
把得到对象作为参数进行相关调用。
$provider.provider是一种定义服务的方法,$provider还提供了很多很多简便的方法,这些方法还被module所引用。
也许这里有人问:control层和service层的区别:
1.事务的回滚在service层,比如重定向之类的在control层。
var myApp = angular.module('myApp',[],function($provide){ // 自定义服务
//自定义服务本身就是一个服务,这个服务的数据可以设置也可以返回,而service和factoyr只能返回数据。
$provide.provider('CustomService',function(){ this.$get = function(){ return { message : 'CustomService Message' } } }); $provide.provider('CustomService2',function(){ this.$get = function(){ return { message : 'CustomService2 Message' } } }); }); myApp.controller('firstController',function(CustomService,$scope,CustomService2){ $scope.name = '张三'; console.log(CustomService2); });
12. 多个控制器数据的共享两种方法:
$scope.data = $scope.$$prevSibling.data; 这样子必须是一个对象的值,在html页面的显示,必须是一个data.name这种形式,如果是data的话那么就不行。
<div ng-app="myApp"> <div ng-controller="firstController"> first.data <input type="text" ng-model="data.name" /> first.Data <input type="text" ng-model="Data.message" /> <p> first-name:{{data.name}} </p> <p> first-message:{{Data.message}} </p> </div> <div ng-controller="secondController"> <p> second-name:{{data.name}} </p> <p> second-message:{{Data.message}} </p> </div> </div>
angular.module('myApp',[]) .factory('Data',function(){ // this.$get = function(){} return { message : '共享的数据' }; }) .controller('firstController',function($scope,Data){ $scope.data = { name : '张三' }; $scope.Data = Data; }) .controller('secondController',function($scope,Data){ $scope.data = $scope.$$prevSibling.data; $scope.Data = Data; });
13. 过滤器:
angular.module('myApp',[]) .factory('Data',function(){ return { message : '共享的数据' }; }) .controller('firstController',function($scope,Data,$filter){ $scope.data = Data; $scope.today = new Date; })
<div ng-controller="firstController"> <!--123,456,789--> <p>{{123456789 | number}}</p> <!--12,345.679--> <p>{{12345.6789 | number:3}}</p> <!--$999,999.00--> <p>{{999999 | currency}}</p> <!--rmb999,999.00--> <p>{{999999 | currency:'rmb'}}</p> <p> default:{{ today }} </p> <p> medium: {{ today | date:'medium'}} </p> <p> short:{{ today | date:'short'}} </p> <p> fullDate:{{ today | date:'fullDate'}} </p> <p> longDate:{{ today | date:'longDate'}} </p> <p> mediumDate:{{ today | date:'mediumDate'}} </p> <p> shortDate:{{ today | date:'shortDate'}} </p> <p> mediumTime:{{ today | date:'mediumTime'}} </p> <p> shortTime:{{ today | date:'shortTime'}} </p> <p> year: {{today | date : 'y'}} {{today | date : 'yy'}} {{today | date : 'yyyy'}} </p> <p> month: {{today | date : 'M'}} {{today | date : 'MM'}} {{today | date : 'MMM'}} {{today | date : 'MMMM'}} </p> <p> day: {{today | date : 'd'}} Day in month {{today | date : 'dd'}} Day in week {{today | date : 'EEEE'}} {{today | date : 'EEE'}} </p> <p> hour: {{today | date : 'HH'}} {{today | date : 'H'}} {{today | date : 'hh'}} {{today | date : 'h'}} </p> <p> minute: {{today | date : 'mm'}} {{today | date : 'm'}} </p> <p> second: {{today | date : 'ss'}} {{today | date : 's'}} {{today | date : '.sss'}} </p> <p> {{today | date : 'y-MM-d H:m:s'}} </p> </div>
14. 更多的选择器:
原始数据类型:
<div ng-controller="firstController"> <!--[1,2,3,4,5]--> <p>{{[1,2,3,4,5,6,7] | limitTo:5}}</p> <!--[3,4,5,6,7]--> <p>{{[1,2,3,4,5,6,7] | limitTo:-5}}</p> <!-- hello world --> <p>{{data.message | lowercase}}</p> <!-- HELLO WORLD --> <p>{{data.message | uppercase}}</p> <p> <!-- [{"name":"上海11212","py":"shanghai"}]--> {{ data.city | filter : '上海'}} </p> <p> <!-- []--> {{ data.city | filter : 'name'}} </p> <p> <!-- name":"上海11212","py":"shanghai"},{"name":"北京","py":"beijing"}--> {{ data.city | filter : {py:'g'} }} </p> <p> <!-- name":"上海11212","py":"shanghai"},{"name":"北京","py":"beijing"}--> {{ data.city | filter : checkName }} </p> <p> <!-- [{"name":"北京","py":"beijing"},{"name":"上海11212","py":"shanghai"},{"name":"四川","py":"sichuan"}] --> <!-- 默认顺序是 正序 asc a~z --> {{ data.city | orderBy : 'py'}} <!-- 默认顺序是 反序 desc z~a --> <!-- [{"name":"四川","py":"sichuan"},{"name":"上海11212","py":"shanghai"},{"name":"北京","py":"beijing"}] --> {{ data.city | orderBy : '-py'}} </p>
angular.module('myApp',[]) .factory('Data',function(){ return { message : 'Hello World', city : [ { name:'上海11212', py : 'shanghai' }, { name:'北京', py : 'beijing' }, { name:'四川', py : 'sichuan' } ] }; }) .controller('firstController',function($scope,Data,$filter){ $scope.data = Data; $scope.today = new Date; // 过滤器 var number = $filter('number')(3000); var jsonString = $filter('json')($scope.data); console.log(jsonString); console.log($scope.data); $scope.checkName = function(obj){ if(obj.py.indexOf('h') === -1) return false; return true; } })
15. 自定义的过滤器
<div ng-controller="firstController"> <ul> <li ng-repeat="user in data | filterAge"> {{user.name}} {{user.age}} {{user.city}} </li> </ul> </div>
var myApp = angular.module('myApp', [], function ($filterProvider, $provide, $controllerProvider) { $provide.service('Data', function () { return [ { name: '张三', age: '20', city: '上海' }, { name: '李四', age: '30', city: '北京' } ]; }); $filterProvider.register('filterAge', function () { return function (obj) { var newObj = []; angular.forEach(obj, function (o) { if (o.age > 20) { newObj.push(o); } }); return newObj; } }); $controllerProvider.register('firstController', function ($scope, Data) { $scope.data = Data; }) }) // module.filter .filter('filterCity',function(){ return function(obj){ var newObj = []; angular.forEach(obj, function (o) { if (o.city === '上海') { newObj.push(o); } }); return newObj; } })
16.正确的使用controller
正确的使用controller
controller不应该尝试做太多的事情。它应该仅仅包含单个视图所需要的业务逻辑,保持controller的简单性,常见办法是抽出不属于controller的工作到service中,在controller通过依赖注入来使用service。
不要在controller中做以下的事情:
1.任何类型的DOM操作-controller应该仅仅包含业务逻辑,任何表现逻辑放到controller中,大大地影响了应用逻辑的可测试性。angular为了自动操作(更新)DOWM,提供的数据绑定。如果希望执行我们自定义的DOM操作,可以把表现逻辑抽取到directive中。
2.input formatting(输入格式化) 使用angular form controls代替。
3.output filtering(输出格式化过滤) 使用angular filters.
4.执行无状态或有状态的,controller共享的代码--使用angular services代替。
5. 实例化或者管理其他组件的生命周期。(例如创建一个服务实例)
17.代码的显示注入和隐式注入
var myApp = angular.module('myApp', [], ['$filterProvider', '$provide', '$controllerProvider', function (a, b, c) { console.log(a, b, c); }]) . factory('CustomService', ['$window', function (a) { console.log(a); }]) // 隐示的依赖注入 .controller('firstController', function ($scope, CustomService) { console.log(CustomService); }) // 显示的依赖注入 .controller('secondController', ['$scope', '$filter', function (a, b) { console.log(b('json')([1, 2, 3, 4, 5])); }]); function otherController(a) { console.log(a); } otherController.$inject = ['$scope'];
18. 内置指令:
渲染指令:
ng-init
ng-bind
ng-repeat
$index 当前索引
$first 是否为头元素
$middle 是否为非头非尾元素
$lasth是否为尾元素
ng-include
ng-bing-template.
ng-bind
ng:bind
data-ng-bind
x-ng-bind
事件指令:
ng-change
ng-click
ng-dblclick
ng-mousedown
ng-mouseenter
ng-mouseleave
ng-mousemove
ng-mouseover
ng-mouseup
ng-submit
节点指令
ng-style
ng-class
ng-class-even
ng-class-odd
这里的red表示style中的类,而-even表示在偶数列会添加这个class.
ng-show
ng-hide
ng-switch
ng-src
ng-href
ng-if
==
.red{color:red;}
ng-class='{red:status}'
什么是指令:
可以利用指令来扩展HTML标签,增加声明式语法来实现想做的任何事,可以对应用有特殊意义的元素和属性来替换一般的HTML标签。
angular 也内置了非常堵偶的指令,ng-app ,ng-controller.
ngsrc 延迟加载。等angular加载完然后加载。这个属性。
<div ng-app="myApp"> <div ng-controller="firstController"> <p>{{1+1}}</p> <p ng-bind="1+1">2</p> <p ng-bind-template="{{1+1}}"></p> <!-- $scope.cityArr = ['上海','北京','杭州'] --> <ul ng-class="{red:status}" ng-init="cityArr = ['上海','北京','杭州']"> <li ng-class-even="'偶数'" ng-class-odd="'奇数'" ng-repeat="city in cityArr" > <span> index:{{$index}} </span> <span> first:{{$first}} </span> <span> middle:{{$middle}} </span> <span> last :{{$last}} </span> <span> {{city}} </span> </li> </ul> <div ng-include="'other.html'"> </div> <div ng-include src="'other.html'"> </div> </div> </div>
var myApp = angular.module('myApp', []) .controller('firstController', function ($scope) { $scope.status = false; $scope.changeStatus = function (event) { // 通过element转换成 jquery对象 angular.element(event.target).html('切换状态为:' + $scope.status); $scope.status = !$scope.status; } $scope.defaultStyle = { color: 'red', 'margin-top': '50px' }; $scope.src = 'http://www.angularjs.org/img/AngularJS-large.png'; })
$scope.changeStatus 这个会自动触发脏检查。
ng-style="{color:'red','margin-top':'50px'}" 后边这个注意加单引号。
19. 自定义的指令:
自定义过滤器,factory,service.
templateUrl:加载模板所要使用的URl
可加载当前模板对应的text/ng-template script id
在使用chrome浏览器时,‘同源策略’会阻止chrome从file://中加载模板,并显示一个"Acces-Control-Allow-Origin"不允许源为null,可以把项目放在服务器上加载,命令为:chrome -allow-file-access-from-files
<div ng-app="myApp"> <custom-tags>1212</custom-tags> <div class="custom-tags"> </div> <div custom-tags> </div> <!-- directive:custom-tags --> </div>
var myApp = angular.module('myApp', [], ['$compileProvider',function ($compileProvider) { $compileProvider.directive('customTags',function(){ return { restrict:'ECAM', template:'<div>custom-tags-html</div>', replace:true //这个属性对于E这种指令适用,可以提现出来 } }); }]) //.directive('') 这是一种简单的写法
20.transclude:true保留原始数据
21.优先级(不是很重要)
priority && terminal
priority 设置指令在模板中的执行顺序,顺序是相对于元素上其他执行而言,默认为0,从大到小的顺序依次执行。
设置优先级的情况比较少,象ng-repest,在遍历元素的过程中,需要angular先拷贝生成的模板元素,在应用其他指令,所以ng-repeat默认的priority是1000
terminal是否当前指令的权重为结束界限,如果这值设置为true,则节点中权重小于当前指令的其他指令不会被执行。相同权重的会执行。
22.
angularjs指令编译三阶段
1.标准浏览器API转化。
将html转化成dom,所以自定义的html标签必须符合html的格式。
2.angular compile
搜索匹配directive,按照priority排序,并执行directive上的compile方法。
3.angular link
执行directive上的link方法,进行scope绑定及事件绑定。
directive可以配置的指令:
priority
template
terminal
templateUrl
scope
replace: 是否替换当前html标签
controller
transclude
controllerAs
compile
require
link
restrict
23.
compile
compile:function(tElement,eAttrs,transclude)
compile函数用来对模板自身进行转换,仅仅在编译阶段运行一次,
compile中直接返回的函数是postLink,表示参数需要执行的函数,也可以返回一个对象里边包含preLink和postLink,
当定义compile参数时,将无视link参数,因为compile里返回的就是该指令需要执行的link函数。
24.
一个标签有两个指令的话,最好有一个template(多的情况不常用)
compile返回的就是link函数,所以说定义了compile就不用定义link.
compile的三个参数,(tElement,eAttrs,transclude)
eElement是angular内置的jquery的一个对象所以可以调用eElement.append的方法。
compile 和link的使用时机
compile想再dom渲染前对它进行变形,并且不需要scope参数想在所有相同directive里共享某些方法,这时应该定义compile里,性能会比较好,返回值就是link的function,这时就是共同使用的时候。
link 对特定的元素注册事件。
需要用到scope参数来实现dom元素的一些行为。
如果在你的directive的返回不是一个对象,而是返回的是一个匿名方法的话,这个方法就是postLink方法。或者说就是Link,link就是postLink。
link(scope,iElement,iAttrs, controller)
link参数代表的是compile返回的postLink
preLink表示在编译阶段之后,指令练级到子元素之前运行。
postLink 表示会在所有子元素指令都连接之后才运行
link函数负责在模型和视图之间进行动态关联,对于每个指令的每个实例,link函数都会执行一次。
25.
<div ng-app="myApp"> <div ng-controller="firstController"> <!-- 1. div 转换为dom结构 2. 默认的优先级为0,哪个先定义哪个先使用 --> <div ng-repeat="user in users" custom-tags="" custom-tags2> </div> </div> </div>
var i = 0; var myApp = angular.module('myApp', []) .directive('customTags',function(){ return { restrict : 'ECAM', template : '<div>{{user.name}}</div>', replace : true, compile:function(tElement,tAttrs,transclude){ tElement.append(angular.element('<div>{{user.name}}{{user.count}}</div>')); // 编译阶段... console.log('customTags compile 编译阶段...'); return { // 表示在编译阶段之后,指令连接到子元素之前运行 pre:function preLink(scope,iElement,iAttrs,controller){ console.log('customTags preLink..') }, // 表示在所有子元素指令都连接之后才运行 post:function postLink(scope,iElement,iAttrs,controller){ iElement.on('click',function(){ scope.$apply(function(){ scope.user.name = 'click after'; scope.user.count = ++i; // 进行一次 脏检查 }); }) console.log('customTags all child directive link..') } } // 可以直接返回 postLink // return postLink function(){ // console.log('compile return fun'); //} }, // 此link表示的就是 postLink link:function(){ // iElement.on('click',function(){ // scope.$apply(function(){ // scope.user.name = 'click after'; // scope.user.count = ++i; // // 进行一次 脏检查 // }); // }) } } }) .directive('customTags2',function(){ return { restrict : 'ECAM', replace : true, compile:function(){ // 编译阶段... console.log('customTags2 compile 编译阶段...'); return { // 表示在编译阶段之后,指令连接到子元素之前运行 pre:function preLink(){ console.log('customTags2 preLink..') }, // 表示在所有子元素指令都连接之后才运行 post:function postLink(){ console.log('customTags2 all child directive link..') } } } } }) .directive('customTags3',function(){ // return postLink; return function(){ } }) .controller('firstController', ['$scope', function ($scope) { $scope.users = [ { id:10, name:'张三' }, { id:20, name:'李四' } ]; }]);
26.
controller && controllerAds && require controller他会暴露一个API,利用这个API可以在多个指令之间通过依赖注入进行通信 controller($scope,$element,$transclude) controllerAs是给controller起个别名,方便使用 require可以将其他指令传递给自己 directiveName: 通过驼峰法的命名指定了控制器应该带有哪一条指令,默认会从同一个元素上的指令 ^directiveName: 在父级朝找指令 ?directiveName:表示指令是可选的,如果找不到,不需要抛出移除。
27.自定义指令:controllerAs 和link,可以使用上面的controller中的属性方法
angular.module('myApp', []) .directive('bookList', function () { return { restrict: 'ECAM', controller: function ($scope) { $scope.books = [ { name: 'php' }, { name: 'javascript' }, { name: 'java' } ]; $scope.addBook = function(){ } this.addBook = function(){ // ... } }, controllerAs:'bookListController',//上边 controller的别名,下边的link或者compile可以使用 template: '<ul><li ng-repeat="book in books">{{book.name}}</li></ul>', replace:true, link:function(scope,iEelement,iAttrs,bookListController){
//上边的iEelement表示的是template中的内容和jquery中的一样,bookListController表示的上边的controllerAs,
// 如果调用this.addBook,你直接用bookListController.addBook,如果调$scope.addBook
iEelement.on('click',bookListController.addBook) } } })
.controller('firstController', ['$scope', function ($scope) { // console.log($scope);
//这里写的controller代码和上边的controller写是一样的效果
}]);
28. require的用法
记住1.require后边的需要有一个引号。2.后面也需要一个引号。3.addBook方法是写在this上而不是写在scope上额。
angular.module('myApp', []) .directive('bookList', function () { return { restrict: 'ECAM', controller: function ($scope) { $scope.books = [ { name: 'php' }, { name: 'javascript' }, { name: 'java' } ]; this.addBook = function(){ $scope.$apply(function(){ $scope.books.push({ name:'Angularjs' }) }); } }, controllerAs:'bookListController', template: '<div><ul><li ng-repeat="book in books">{{book.name}}</li></ul><book-add></book-add></div>', replace:true } }) .directive('bookAdd',function(){ return { restrict:'ECAM', require:'^bookList', template:'<button type="button">添加</button>', replace:true, link:function(scope,iElement,iAttrs,bookListController){ iElement.on('click',bookListController.addBook); } } }) .controller('firstController', ['$scope', function ($scope) { // console.log($scope); }]);
这里我们可以看到require可以继承父类的controller方法,和属性。注意^bookList继承父类的。
require的作用是为了让父子指令或者兄弟指令的controller之间搭建一个桥梁
也就是说父指令里的controller里面的数据能分享给子指令的controller
其中子指令的link第四个参数的值是父指令的controller对象的作用域上下文
require有两个修饰符号:”?”、”^”
? : 如果require没有找到相应的指令避免报错,还能确保程序的正常执行
^ : 表示往父级查找
?^可以一起使用
29.scope 中@只能是值的绑定(简单数据类型的绑定)而&却可以是对象的绑定。注意对象html页面中的内容。如果是一个对象的话,我们需要先把父类的controller中的东西放到一个属性中,然后我们的子controller然后从html标签中的属性中取值,然后付给自己的属性。
scope(是针对子controller与父controller,但是我们需要注意的时候与reqiuire的区别是,require为了link中的第四个参数提供服务,而我们的scope则需要把属性绑定到html的属性上边)
这里我们可以看出来,如果books是object类型,而title是简单数据类型。
scope:为当前指令创建一个新的作用域,而不是使之继承父作用域 默认值记不清了 false继承父元素的作用域,和父共享一个作用域 true创建一个新的作用域,但是还是会继承父作用域的属性, object也是创建一个独立的scope但是也可以通过一些方式继承父类 object:参数 & 作用域把父作用域的属性包装成一个函数,从而以函数的方法会读写父作用域的属性。 注意这里的单项绑定是方法,下边的双向绑定确实一个属性。 =:作用域的属性与父作用域的属性进行双向绑定,任何一方的修改均影响到对方。 @: 只能读取父作用域里的值单向绑定。
<div ng-app="myApp"> <div ng-controller="firstController"> {{ books }} <div book-list books="books" parent-books="books" parent-title="{{title}}"> <!--注意这里的title有{{}},简单数据类型绑定属性的时候有,而负责的数据类型则没有--> </div> </div> </div>
angular.module('myApp', []) .directive('bookList', function () { return { restrict: 'ECAM', controller: function ($scope) { // &books // $scope.books = $scope.a(); // =books; // $scope.books = $scope.b; // $scope.b.push({name:'nodejs'}); console.log($scope.c); }, // 创建一个有继承链的独立作用域 // scope:true, // 当为对象的时候也会创建一个独立的作用域 scope:{ // 将父元素books封装成一个a函数 // a:'&books' // 双向绑定 b = parentBooks属性对应的父作用域的表达式 // b:'=parentBooks' // 使用简单数据类型的方法 c:'@parentTitle' }, controllerAs:'bookListController', template: '<div><ul><li ng-repeat="book in books">{{book.name}}</li></ul></div>', replace:true } }) .controller('firstController', ['$scope', function ($scope) { console.log($scope); $scope.books = [ { name: 'php' }, { name: 'javascript' }, { name: 'java' } ]; $scope.title = '张三'; }]);
30.自定义
嵌套的directive
heading:@heading 可以简写成heading:@
<div ng-app="myApp"> <div class="container"> <div ng-controller="firstController"> <kittencup-group> <kittencup-collapse ng-repeat="collapse in data" heading="{{collapse.title}}"> {{collapse.content}} </kittencup-collapse> </kittencup-group> </div> </div> </div>
angular.module('myApp', []) // 数据 .factory('Data', function () { return [ { title: 'no1', content: 'no1-content' }, { title: 'no2', content: 'no2-content' }, { title: 'no3', content: 'no3-content' } ]; }) // 控制器 .controller('firstController', ['$scope','Data',function ($scope,Data) { $scope.data = Data; }]) .directive('kittencupGroup',function(){ return { restrict:'E', replace:true, template:'<div class="panel-group" ng-transclude></div>', transclude:true, controllerAs:'kittencupGroupContrller', controller:function(){ this.groups = []; this.closeOtherCollapse = function(nowScope){ angular.forEach(this.groups,function(scope){ if(scope !== nowScope){ scope.isOpen = false; } }) } } } }) .directive('kittencupCollapse',function(){ return { restrict:'E', replace:true, require:'^kittencupGroup', templateUrl:'app/tmp/kittencupCollapse.html', scope:{ heading:'@' }, link:function(scope,element,attrs,kittencupGroupContrller){ scope.isOpen = false; scope.changeOpen = function(){ scope.isOpen = !scope.isOpen; kittencupGroupContrller.closeOtherCollapse(scope); } kittencupGroupContrller.groups.push(scope); }, transclude:true } })
一段angular指令自己平时练习的代码:
var ng = angular.module('box',[]); ng.directive('aa',function(){ return { restrictive:"ECAM", template:"<div><h2>this is our world {{data}}</h2><span ng-transclude></span><bb data='data' title='{{title}}'></bb></div>", replace: true, transclude:true, controller:function($scope){ $scope.data = {'key':"beautiful"} this.al = function(){ alert(1234); } $scope.title = "title"; }, controllerAs:"aacontroller" } }) ng.controller('first',function($scope){ }); ng.directive('bb',function(){ return{ restrictive:"ECAM", require:'^?aa', template:"<div>this is son directive {{data}}<input ng-model='data'/></div>", replace: true, controller:function($scope){ $scope.data = $scope.a; }, scope:{ a:'@title' }, link:function(scope,el,attr,aacontroller){ //el.on('click',aacontroller.al); } } });
32.优先级,小于0的不执行
<div ng-controller="firstController"> <custom-tags>原始数据</custom-tags> <div custom-tags2 custom-tags3> </div> </div>
var myApp = angular.module('myApp', []) .directive('customTags', function () { return { restrict: 'ECAM', template:'<div>新数据 <span ng-transclude></span></div>', replace: true, transclude:true } }) .directive('customTags2', function () { return { restrict: 'ECAM', template:'<div>2</div>', replace: true, priority:-1 } }) .directive('customTags3', function () { return { restrict: 'ECAM', template:'<div>3</div>', replace: true, priority: 0, // 小于0的directive 都不会执行 terminal:true } }) .controller('firstController', ['$scope', function ($scope) { $scope.name = '张三'; }]);
33. $scope相当于js对象继承的作用域链
34.
$provide方法都有快捷方法。
constant(name,object)
此方法首先运行,可以用它来声明整个应用范围内的常量,并且让它们在所有配置(config方法里)和实例(controller,service等)方法中都可用。
run(initializationFn)
想要在注入启动之后执行某些操作,而这些操作需要在页面对用户可用之前执行,可以使用此方法。
比如 加载远程的模板,需要在使用前放入缓存,或者在使用操作前判断用户是否登录,未登录可以先去登录页面。
angular.module('myApp',[],['$provide',function($provide){ console.log('config'); // $provide.factory // $provide.service // $provide.constant // $provide.value; }]) .config(function(APIKEY){ console.log(APIKEY); console.log('config'); }) // 在config之后controller等其他服务之前。。 .run(function(){ console.log('run'); }) // 它只是可以注入任何方法 .constant('APIKEY','xxxx') // 只能注入controller...service factory .value('vension','1.0.0') .controller('firstController',['APIKEY','vension',function(APIKEY,vension){ console.log(APIKEY); console.log(vension); console.log('controller'); }]);
35. ng-show ng-hide ng-if 可以看:http://m.blog.csdn.net/article/details?id=44701769
angularJS中的ng-show、ng-hide、ng-if指令都可以用来控制dom元素的显示或隐藏。ng-show和ng-hide根据所给表达式的值来显示或隐藏HTML元素。当赋值给ng-show指令的值为false时元素会被隐藏,值为true时元素会显示。ng-hide功能类似,使用方式相反。元素的显示或隐藏是通过改变CSS的display属性值来实现的。
ng-if指令可以根据表达式的值在DOM中生成或移除一个元素。如果赋值给ng-if的表达式的值是false,那对应的元素将会从DOM中移除,否则生成一个新的元素插入DOM中。ng-if同no-show和ng-hide指令最本质的区别是,它不是通过CSS显示或隐藏DOM节点,而是删除或者新增结点。
36.服务返回一种方法的时候 http://www.runoob.com/try/try.php?filename=try_ng_services_filter2
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="https://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script> </head> <body> <div ng-app="myApp" ng-controller="myCtrl"> <p>在获取数组 [255, 251, 200] 值时使用过滤器:</p> <ul> <li ng-repeat="x in counts">{{x | myFormat}}</li> </ul> <p>过滤器使用服务将10进制转换为16进制。</p> </div> <script> var app = angular.module('myApp', []); app.service('hexafy', function() { this.myFunc = function (x) { return x.toString(16); } }); app.filter('myFormat',['hexafy', function(hexafy) { return function(x) { return hexafy.myFunc(x); }; }]); app.controller('myCtrl', function($scope) { $scope.counts = [255, 251, 200]; }); </script> </body> </html>
一下就是自己angular开发中,常遇到的一些问题
37.如果ul li中的li点击事件,本来想在Link中直接写一个类型ul.find('li').on的发现不行的,只能在页面的li中写一个方法,并且传入$event.利用
// 通过element转换成 jquery对象
angular.element(event.target).html('切换状态为:' + $scope.status);
38.$scope与this的区别。
(1)$scope会继承,但是this不会。
(2)在两个directive中,如果一个directive通过require引用另外一个directive中的东西,那么方法必须写在this中,如果写在$scope中则不行。
39.const run value这个方法的区别和联系。
config先执行,而run在config之后在controller等其他服务之前。。
const value
但是const只能注入方法中,
value// 只能注入controller...service factory
40.transclude:true实现的在于必须外边的controller是有定义的,如果外边的controller没有定义没效果.
41.AngularJS的学习 $on、$emit和$broadcast的使用
不过用的是$scope 发射和接受。
emit(name,data)向父control发射的。
而broadcast(name,data)是向子control发射的。