angular framework 知识点 开发实践
第一点: 理解 angularjs 中的数据绑定
<div ng-controller="MyController">
<span style="white-space:pre"> </span><h1>来自controller scope My:{{name}}</h1>
</div>
<h1>来自 rootscope {{name}}</h1>
testapp.controller('MyController',function($scope, $parse){
$scope.name = "ARI";
});
第二个:理解angularjs 的模块和模块的加载机制
<span style="color:#330033;">angular.module('myApp', []);
//这就是 angular 中定义模块的 方式
这个方法接受两个参数,第一个是名字name,这里 name = myAPP 第二个参数是一个数组,这个里面存放的是其他模块的名字myApp这个模块 依赖这些模块</span>
那么问题来了 这些模块应该怎么加载呢,他们之间顺序变化有问题吗? 这里就涉及到angularjs 中的模块加载机制
<span style="color:#330033;">function SomeClass(greeter) {
this.greeter = greeter;
}
SomeClass.prototype.greetName = function(name) {
this.greeter.greet(name);
};</span>
SomeClass能够在运行时访问到内部的greeter,但它并不关心如何获得对greeter的引用。
为了获得对greeter实例的引用,SomeClass的创建者会负责构造其依赖关系并传递进去。
angularjs 通过一个 $injector 的一个组件 来去实现各种情况下模块的依赖和注入
下面我们来简单看一下 angularjs 的 $injector api
标函数中。annotate()方法可以帮助$injector判断哪些服务会在函数被调用时注入进去。
annotate()方法可以接受一个参
值是在调用时被注入到目标函数中的服务的名称。
<span style="color:#330033;">var injector = angular.injector(['ng', 'myApp']);
injector.annotate(function($q, greeter) {});
// ['$q', 'greeter']</span>
第二个 get() 方法
true,否则返回false。它能接受一个参数:
并将所有参数都传递给构造函数。它可以接受两个参数:
invoke()方法会调用方法并从$injector中添加方法参数。
invoke()方法接受三个参数。
fn(function)
这个函数就是要调用的函数。这个函数的参数由函数声明设置。
self (object-可选)
self参数允许我们设置调用方法的this参数。
locals (object-可选)
这个可选参数提供另一种方式在函数被调用时传递参数名给该函数。
invoke()方法返回fn函数返回的值
第三点 angularjs 中的作用域
对象是定义应用业务逻辑、控制器方法和视图属性的地方
和作用域进行连接,然后应用会对DOM进行设置以便将属性变化通知给AngularJS
也可以依赖$scope在其发生变化时立刻重新渲染视图
以引用父级$scope中的属性
可以将数据模型的变化通知给整个应用,甚至是系统外的组件;
可以进行嵌套,隔离业务功能和数据;
给表达式提供运算时所需的执行环境。
$scope 的生命周期:
制器或指令运行时将作用域传递进去。
象的函数也会将自身附加到视图中。这些作用域将会注册当Angular应用上下文中发生变化时需
要运行的函数。
都执行自己的脏值检测。每个监控函数都会检查变化。如果检测到任意变化,$scope对象就会触
发指定的回调函数。
尽管永远不会需要清理作用域(因为Angular会为你处理),但是知道是谁创建了这个作用域
还是有用的,因为你可以使用这个$scope上叫做$destory()的方法来清理这个作用域。
第四个:理解 angularjs 中的控制器
<span style="color:#330033;">var app = angular.module('app', []);
app.controller('FirstController', function($scope) {
$scope.message = "hello";
});</span>
设计良好的应用会将复杂的逻辑放到指令和服务中。通过使用指令和服务,我们可以将控制
器重构成一个轻量且更易维护的形式:
第五个:熟悉 angularjs中的过滤器,表达式
常重要的。
前面已经见过使用表达式的示例。用{{ }}符号将一个变量绑定到$scope上的写法本质上就
是一个表达式:{{ expression }}。当用$watch进行监听时,AngularJS会对表达式或函数进行
运算。
表达式和eval(javascript)非常相似,但是由于表达式由AngularJS来处理,它们有以下显
著不同的特性:
所有的表达式都在其所属的作用域内部执行,并有访问本地$scope的权限;
如果表达式发生了TypeError和ReferenceError并不会抛出异常;
不允许使用任何流程控制功能(条件控制,例如if/eles);
可以接受过滤器和过滤器链。
对表达式进行的任何操作,都会在其所属的作用域内部执行,因此可以在表达式内部调用那
些限制在此作用域内的变量,并进行循环、函数调用、将变量应用到数学表达式中等操作。
过滤器用来格式化需要展示给用户的数据。AngularJS有很多实用的内置过滤器,同时也提
供了方便的途径可以自己创建过滤器。
在调用过滤器时简单地把input当作字符串来处理。
<span style="color:#330033;">angular.module('myApp.filters', [])
.filter('capitalize', function() {
return function(input) {
// input是我们传入的字符串
if (input) {
return input[0].toUpperCase() + input.slice(1);
}
});</span>
第六个:理解angularjs中的指令
<span style="color:#330033;">angular.module('myApp', [])
.directive('myDirective', function() {
return {
restrict: String,
priority: Number,
terminal: Boolean,
template: String or Template Function:
function(tElement, tAttrs) (...},
templateUrl: String,
replace: Boolean or String,
scope: Boolean or Object,
transclude: Boolean,
controller: String or
function(scope, element, attrs, transclude, otherInjectables) { ... },
controllerAs: String,
require: String,
link: function(scope, iElement, iAttrs) { ... },
compile: // 返回一个对象或连接函数,如下所示:
function(tElement, tAttrs, transclude) {
return {
pre: function(scope, iElement, iAttrs, controller) { ... },
post: function(scope, iElement, iAttrs, controller) { ... }
}
// 或者
return function postLink(...) { ... }
}
};
});</span>
E: element A:attribute C:class M: anotation
默认是通过 属性的方式
priority : 优先级
当有多个directive定义在同一个DOM元素时,有时需要明确它们的执行顺序。这属性用于在directive的compile function调用之前进行排序。如果优先级相同,则执行顺序是不确定的(经初步试验,优先级高的先执行,同级时按照类似栈的“后绑定先执行”。
terminal :
如果设置为”true”,则表示当前的priority将会成为最后一组执行的directive。任何directive与当前的优先级相同的话,他们依然会执行,但顺序是不确定的(虽然顺序不确定,但基本上与priority的顺序一致。当前优先级执行完毕后,更低优先级的将不会再执行)。
template:
如果replace 为true,则将模版内容替换当前的HTML元素,并将原来元素的属性、class一并迁移;如果为false,则将模版元素当作当前元素的子元素处理。
一个可以接受两个参数的函数,参数为tElement和tAttrs,并返回一个代表模板的字符串。tElement和tAttrs中的t代表template,是相对于instance的。
<span style="color:#330033;"> angular.module('app',[])
.directive('myDirective', function () {
return {
restrict: 'E',
template: '<a href="http://www.baidu.com">百度</a>'
};
})</span>
<span style="color:#330033;"> HtmlCode:
<my-directive></my-directive></span>
<span style="color:#330033;">angular.module('app',[])
.directive('myDirective', function () {
return {
restrict: 'EAC',
template: function (elem, attr) {
return "<a href='" + attr.value + "'>" + attr.text + "</a>";
}
};
})
</span>
templateUrl:
与template基本一致,但模版通过指定的url进行加载。因为模版加载是异步的,所以compilation、linking都会暂停,等待加载完毕后再执行。
replace:
如果设置为true,那么模版将会替换当前元素,而不是作为子元素添加到当前元素中。
scope:
scope参数是可选的,可以被设置为true或一个对象。默认值是false。
scope:false 此时,directive没有独立的scope对象,link函数中引用的scope对象来自于当前节点的默认controller
scope:true 此时,directive拥有独立的scope对象,此scope是由父scope对象继承而来,可以访问父scope中的所有属性,此时实际上是通过继承链接(prototype链)访问的父scope中的属性,要注意的是,当给此scope继承而来的属性名称赋值的时候,子scope中会相应建立一个本地属性,此时改变的是本scope的变量属性,父scope中的属性是不会改变的。
下面 我们来说说 directive 与scope 的 隔离交互
AngularJS 的 directive 默认能共享父 scope 中定义的属性,例如在模版中直接使用父 scope 中的对象和属性。通常使用这种直接共享的方式可以实现一些简单的 directive 功能。当你需要创建一个可重复使用的 directive,只是偶尔需要访问或者修改父 scope 的数据,就需要使用隔离 scope。当使用隔离 scope 的时候,directive 会创建一个没有依赖父 scope 的 scope,并提供一些访问父 scope 的方式。
当你想要写一个可重复使用的 directive,不能再依赖父 scope,这时候就需要使用隔离 scope 代替。共享 scope 可以直接共享父 scope,而隔离 scope 无法共享父scope。下图解释共享 scope 和隔离 scope 的区别:
使用共享 scope 的时候,可以直接从父 scope 中共享属性。因此下面示例可以将那么属性的值输出出来。使用的是父 scope 中定义的值。
<span style="color:#330033;">app.controller("myController", function ($scope) {
$scope.name = "hello world";
}).directive("shareDirective", function () {
return {
template: 'Say:{{name}}'
}
});</span>
<span style="color:#330033;"><div ng-controller="myController">
<div share-directive=""></div>
</div></span>
结果输出 hello world
使用隔离 scope 的时候,无法从父 scope 中共享属性。因此下面示例无法输出父 scope 中定义的 name 属性值。
<span style="color:#330033;">app.controller("myController", function ($scope) {
$scope.name = "hello world";
}).directive("isolatedDirective", function () {
return {
scope: {},
template: 'Say:{{name}}'
}
});</span>
<span style="color:#330033;"><div ng-controller="myController">
<div isolated-directive=""></div>
</div></span>
输出结果 say:
从上面看出共享 scope 允许从父 scope 渗入到 directive 中,而隔离 scope 不能,在隔离 scope 下,给 directive 创造了一堵墙,使得父 scope 无法渗入到 directive 中。
在 Directive 中创建隔离 scope 很简单,只需要定义一个 scope 属性即可,这样,这个 directive 的 scope 将会创建一个新的 scope,如果多个 directive 定义在同一个元素上,只会创建一个新的 scope。
<span style="color:#330033;">angular.module('app').controller("myController", function ($scope) {
$scope.user = {
id:1,
name:"hello world"
};
}).directive('isolatedScope', function () {
return {
scope: {},
template: 'Name: {{user.name}} Street: {{user.addr}}'
};
});</span>
现在 scope 是隔离的,user 对象将无法从父 scope 中访问,因此,在 directive 渲染的时候 user 对象的占位将不会输出内容。
directive 在使用隔离 scope 的时候,提供了三种方法同隔离之外的地方交互。这三种分别是
@ 绑定一个局部 scope 属性到当前 dom 节点的属性值。结果总是一个字符串,因为 dom 属性是字符串。
& 提供一种方式执行一个表达式在父 scope 的上下文中。如果没有指定 attr 名称,则属性名称为相同的本地名称。
= 通过 directive 的 attr 属性的值在局部 scope 的属性和父 scope 属性名之间建立双向绑定。
如下示例:directive 声明未隔离 scope 类型,并且使用@绑定 name 属性,在 directive 中使用 name 属性绑定父 scope 中的属性。当改变父 scope 中属性的值的时候,directive 会同步更新值,当改变 directive 的 scope 的属性值时,父 scope 无法同步更新值。
<span style="color:#330033;"> app.controller("myController", function ($scope) {
$scope.name = "hello world";
}).directive("isolatedDirective", function () {
return {
scope: {
name: "@"
},
template: 'Say:{{name}} <br>改变隔离scope的name:<input type="buttom" value="" ng-model="name" class="ng-pristine ng-valid">'
}
})</span>
<span style="color:#330033;"><div ng-controller="myController">
<div class="result">
<div>父scope:
<div>Say:{{name}}<br>改变父scope的name:<input type="text" value="" ng-model="name"/></div>
</div>
<div>隔离scope:
<div isolated-directive name="{{name}}"></div>
</div>
<div>隔离scope(不使用{{name}}):
<div isolated-directive name="name"></div>
</div>
</div></span>
= 局部 scope 属性
= 通过 directive 的 attr 属性的值在局部 scope 的属性和父 scope 属性名之间建立双向绑定。
意思是,当你想要一个双向绑定的属性的时候,你可以使用=来引入外部属性。无论是改变父 scope 还是隔离 scope 里的属性,父 scope 和隔离 scope 都会同时更新属性值,因为它们是双向绑定的关系。
<span style="color:#330033;"> app.controller("myController", function ($scope) {
$scope.user = {
name: 'hello',
id: 1
};
}).directive("isolatedDirective", function () {
return {
scope: {
user: "="
},
template: 'Say:{{user.name}} <br>改变隔离scope的name:<input type="buttom" value="" ng-model="user.name"/>'
}
})</span>
<span style="color:#330033;"><div ng-controller="myController">
<div>父scope:
<div>Say:{{user.name}}<br>改变父scope的name:<input type="text" value="" ng-model="user.name"/></div>
</div>
<div>隔离scope:
<div isolated-directive user="user"></div>
</div>
<div>隔离scope(使用{{name}}):
<div isolated-directive user="{{user}}"></div>
</div>
</div></span>
& 局部 scope 属性
& 方式提供一种途经是 directive 能在父 scope 的上下文中执行一个表达式。此表达式可以是一个 function。
比如当你写了一个 directive,当用户点击按钮时,directive 想要通知 controller,controller 无法知道 directive 中发生了什么,也许你可以通过使用 angular 中的 event 广播来做到,但是必须要在 controller 中增加一个事件监听方法。
最好的方法就是让 directive 可以通过一个父 scope 中的 function,当 directive 中有什么动作需要更新到父 scope 中的时候,可以在父 scope 上下文中执行一段代码或者一个函数。
<span style="color:#330033;"> app.controller("myController", function ($scope) {
$scope.value = "hello world";
$scope.click = function () {
$scope.value = Math.random();
};
}).directive("isolatedDirective", function () {
return {
scope: {
action: "&"
},
template: '<input type="button" value="在directive中执行父scope定义的方法" ng-click="action()"/>'
}
})</span>
<span style="color:#330033;"> <div ng-controller="myController">
<div>父scope:
<div>Say:{{value}}</div>
</div>
<div>隔离scope:
<div isolated-directive action="click()"></div>
</div>
</div></span>
Transclusion(嵌入)
Transclusion是让我们的指令包含任意内容的方法。我们可以延时提取并在正确的scope下编译这些嵌入的内容,最终将它们放入指令模板中指定的位置。 如果你在指令定义中设置 transclude:true,一个新的嵌入的scope会被创建,它原型继承子父scope。 如果你想要你的指令使用隔离的scope,但是它所包含的内容能够在父scope中执行,transclusion也可以帮忙。
有时候我我们要嵌入指令元素本身,而不仅仅是它的内容。在这种情况下,我们需要使用 transclude:’element’。它和 transclude:true 不同,它将标记了 ng-transclude 指令的元素一起包含到了指令模板中。使用transclusion,你的link函数会获得一个名叫 transclude 的链接函数,这个函数绑定了正确的指令scope,并且传入了另一个拥有被嵌入DOM元素拷贝的函数。你可以在这个 transclude 函数中执行比如修改元素拷贝或者将它添加到DOM上等操作。
transclude:true
<span style="color:#330033;"><body ng-app="myApp">
<div class="AAA">
<hero name="superman">Stuff inside the custom directive</hero>
</div>
</body></span>
<span style="color:#330033;">angular.module('myApp', []).directive('hero', function () {
return {
restrict: 'E',
transclude: true,
scope: { name:'@' },
template: '<div>' +
'<div>{{name}}</div><br>' +
'<div ng-transclude></div>' +
'</div>'
};
});</span>
经过指令编译以后
指令 中的 controller controllerAs require
controller参数可以是一个字符串或一个函数。当设置为字符串时,会以字符串的值为名字,来查找注册在应用中的控制器的构造函数:
<span style="color:#330033;">angular.module('myApp', [])
.directive('myDirective', function() {
restrict: 'A', // 始终需要
controller: 'SomeController'
})
// 应用中其他的地方
angular.module('myApp')
.controller('SomeController', function($scope, $element, $attrs, $transclude) {
// 控制器逻辑放在这里
});</span>
可以在指令内部通过匿名构造函数的方式来定义一个内联的控制器:
<span style="color:#330033;">angular.module('myApp',[])
.directive('myDirective', function() {
restrict: 'A',
controller:
function($scope, $element, $attrs, $transclude) {
// 控制器逻辑放在这里
}
});</span>
我们可以将任意可以被注入的AngularJS服务传递给控制器。例如,如果我们想要将$log服务传入控制器,只需简单地将它注入到控制器中,便可以在指令中使用它了。
控制器中也有一些特殊的服务可以被注入到指令当中。这些服务有:
1. $scope
与指令元素相关联的当前作用域。
2. $element
当前指令对应的元素。
3. $attrs
由当前元素的属性组成的对象。例如,下面的元素:
<div id="aDiv"class="box"></div>
具有如下的属性对象:
{
id: "aDiv",
class: "box"
}
4. $transclude
嵌入链接函数会与对应的嵌入作用域进行预绑定。
transclude链接函数是实际被执行用来克隆元素和操作DOM的函数。
例如,我们想要通过指令来添加一个超链接标签。可以在控制器内的$transclude函数中实现:
<span style="color:#330033;">angular.module('myApp')
.directive('link', function() {
return {
restrict: 'EA',
transclude: true,
controller:
function($scope, $element, $transclude, $log) {
$transclude(function(clone) {
var a = angular.element('<a>');
a.attr('href', clone.text());
a.text(clone.text());
$log.info("Created new a tag in link directive");
$element.append(a);
});
}
};
});</span>
指令的控制器和link函数可以进行互换。控制器主要是用来提供可在指令间复用的行为,但链接函数只能在当前内部指令中定义行为,且无法在指令间复用
由于指令可以require其他指令所使用的控制器,因此控制器常被用来放置在多个指令间共享的动作。
如果我们希望将当前指令的API暴露给其他指令使用,可以使用controller参数,否则可以使用link来构造当前指令元素的功能性。如果我们使用了scope.$watch()或者想要与DOM元素做实时的交互,使用链接会是更好的选择。
技术上讲,$scope会在DOM元素被实际渲染之前传入到控制器中。在某些情况下,例如使用了嵌入,控制器中的作用域所反映的作用域可能与我们所期望的不一样,这种情况下,$scope对象无法保证可以被正常更新。
controller,link,compile有什么不同
<span style="color:#330033;">//directives.js增加exampleDirective
phonecatDirectives.directive('exampleDirective', function() {
return {
restrict: 'E',
template: '<p>Hello {{number}}!</p>',
controller: function($scope, $element){
$scope.number = $scope.number + "22222 ";
},
//controllerAs:'myController',
link: function(scope, el, attr) {
scope.number = scope.number + "33333 ";
},
compile: function(element, attributes) {
return {
pre: function preLink(scope, element, attributes) {
scope.number = scope.number + "44444 ";
},
post: function postLink(scope, element, attributes) {
scope.number = scope.number + "55555 ";
}
};
}
}
});
//controller.js添加
dtControllers.controller('directive2',['$scope',
function($scope) {
$scope.number = '1111 ';
}
]);
//html
<body ng-app="phonecatApp">
<div ng-controller="directive2">
<example-directive></example-directive>
</div>
</body></span>
运行结果:
<span style="color:#330033;">Hello 1111 22222 44444 55555 ! </span>
由结果可以看出来,controller先运行,compile后运行,link不运行(link就是compile中的postLink)。将上例中的compile注释掉运行结果:
<span style="color:#330033;">Hello 1111 22222 33333 ! </span>
由结果可以看出来,controller先运行,link后运行,link和compile不兼容。compile改变dom,link事件的触发和绑定
controllerAs
controllerAs参数用来设置控制器的别名,可以以此为名来发布控制器,并且作用域可以访问controllerAs。这样就可以在视图中引用控制器,甚至无需注入$scope。
例如,创建一个MainController,然后不要注入$scope,如下所示:
<span style="color:#330033;">angular.module('myApp')
.controller('MainController', function() {
this.name = "Ari";
});</span>
现在,在HTML中无需引用作用域就可以使用MainController。
<span style="color:#330033;"><div ng-appng-controller="MainControllerasmain">
<input type="text" ng-model="main.name" />
<span>{{ main.name }}</span>
</div></span>
这个参数看起来好像没什么大用,但它给了我们可以在路由和指令中创建匿名控制器的强大能力。这种能力可以将动态的对象创建成为控制器,并且这个对象是隔离的、易于测试的。
<span style="color:#330033;">angular.module('myApp')
.directive('myDirective', function() {
return {
restrict: 'A',
template: '<h4>{{ myController.msg }}</h4>',
controllerAs: 'myController',
controller: function() {
this.msg = "Hello World"
}
};
});</span>
require
?
如果在当前指令中没有找到所需要的控制器,会将null作为传给link函数的第四个参数。
^
如果添加了^前缀,指令会在上游的指令链中查找require参数所指定的控制器。
?^
将前面两个选项的行为组合起来,我们可选择地加载需要的指令并在父指令链中进行查找。
没有前缀
如果没有前缀,指令将会在自身所提供的控制器中进行查找,如果没有找到任何控制器(或具有指定名字的指令)就抛出一个错误。
<span style="color:#330033;">var app = angular.modeule('myapp',[]);
app.directive('common',function(){
return {
...
controller: function($scope){
this.method1 = function(){
};
this.method2 = function(){
};
},
...
}
});
app.directive('d1',function(){
return {
...
require: '?^common',
link: function(scope,elem,attrs,common){
scope.method1 = common.method1;
..
},
...
}
});</span>
ngModal
<span style="color:#330033;">angular.module('myApp')
.directive('myDirective',function(){
return {
require: '?ngModel',
link: function(scope, ele, attrs, ngModel) {
if (!ngModel) return;
// 现在我们的指令中已经有ngModelController的一个实例
}
};
});</span>
如果不设置require选项,ngModelController就不会被注入到指令中。
为了设置作用域中的视图值,需要调用ngModel.$setViewValue()函数。ngModel.$setViewValue()函数可以接受一个参数。
$setViewValue()方法适合于在自定义指令中监听自定义事件(比如使用具有回调函数的jQuery插件),我们会希望在回调时设置$viewValue并执行digest循环。
<span style="color:#330033;">angular.module('myApp')
.directive('myDirective', function() {
return {
require: '?ngModel',
link: function(scope, ele, attrs, ngModel) {
if (!ngModel) return;
$(function() {
ele.datepicker({
onSelect: function(date) {
// 设置视图和调用apply
scope.$apply(function() {
ngModel.$setViewValue(date);
});
}
});
});
}
};
});</span>
看到这里 可能大家还是不明白 what fuck this
<span style="color:#330033;"><!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="angular.js" charset="utf-8"></script>
</head>
<body ng-controller='ctrl'>
<input type="text" test ng-model=_val>
<script>
var app = angular.module('app',[]);
app.controller('ctrl',function ($scope){
$scope._val = "leifengshushu";
})
app.directive('test',function(){
return{
restrict: 'AE',
require: 'ngModel',
link: function (scope,iElem,iAttr,ngmodel){
console.log(ngmodel)
}
}
})
</script>
</body>
</html></span>
这个对象包含很多属性和方法$modelValue为模型值,即赋给ng-model的值(与控制器绑定的值)
两者不一定相等,因为$viewValue同步到$modelValue要经过一系列的操作。
虽然大多数情况下两者是相等的(例如上面的例子)
它会依次执行每一个函数,把每一个函数执行的结果传个下一个函数,而最后一个函数执行的值将会传到model中,我们可以将函数push进去,那样它就会执行。
当数据的模型值发生变化的时候,里面的函数会被一一执行,同样我们就可以将函数push进去,让它执行
$viewChangeListeners的值也是一个由函数组成的数组,当视图的值发生变化的时候里面的函数会被一一调用,
实现跟$watch类似的功能。
$setViewValue用于设置视图值(上面的例子就是将input的value值赋值给$viewValue);
下面我们来说说 $apply() 和 $digest();
探索$apply()和$digest()
AngularJS提供了一个非常酷的特性叫做双向数据绑定(Two-way Data Binding),这个特性大大简化了我们的代码编写方式。数据绑定意味着当View中有任何数据发生了变化,那么这个变化也会自动地反馈到scope的数据上,也即意味着scope模型会自动地更新。类似地,当scope模型发生变化时,view中的数据也会更新到最新的值。那么AngularJS是如何做到这一点的呢?当你写下表达式如{{ aModel }}时,AngularJS在幕后会为你在scope模型上设置一个watcher,它用来在数据发生变化的时候更新view。这里的watcher和你会在AngularJS中设置的watcher是一样的:
<span style="color:#330033;">$scope.$watch('aModel', function(newValue, oldValue) {
//update the DOM with newValue
});</span>
传入到$watch()中的第二个参数是一个回调函数,该函数在aModel的值发生变化的时候会被调用。当aModel发生变化的时候,这个回调函数会被调用来更新view这一点不难理解,但是,还存在一个很重要的问题!AngularJS是如何知道什么时候要调用这个回调函数呢?换句话说,AngularJS是如何知晓aModel发生了变化,才调用了对应的回调函数呢?它会周期性的运行一个函数来检查scope模型中的数据是否发生了变化吗?好吧,这就是$digest循环的用武之地了。
Note: $scope.$apply()会自动地调用$rootScope.$digest()。$apply()方法有两种形式。第一种会接受一个function作为参数,执行该function并且触发一轮$digest循环。第二种会不接受任何参数,只是触发一轮$digest循环。我们马上会看到为什么第一种形式更好。
如果AngularJS总是将我们的代码wrap到一个function中并传入$apply(),以此来开始一轮$digest循环,那么什么时候才需要我们手动地调用$apply()方法呢?实际上,AngularJS对此有着非常明确的要求,就是它只负责对发生于AngularJS上下文环境中的变更会做出自动地响应(即,在$apply()方法中发生的对于models的更改)。AngularJS的built-in指令就是这样做的,所以任何的model变更都会被反映到view中。但是,如果你在AngularJS上下文之外的任何地方修改了model,那么你就需要通过手动调用$apply()来通知AngularJS。这就像告诉AngularJS,你修改了一些models,希望AngularJS帮你触发watchers来做出正确的响应。
比如,如果你使用了JavaScript中的setTimeout()来更新一个scope model,那么AngularJS就没有办法知道你更改了什么。这种情况下,调用$apply()就是你的责任了,通过调用它来触发一轮$digest循环。类似地,如果你有一个指令用来设置一个DOM事件listener并且在该listener中修改了一些models,那么你也需要通过手动调用$apply()来确保变更会被正确的反映到view中
<span style="color:#330033;"><body ng-app="myApp">
<div ng-controller="MessageController">
Delayed Message: {{message}}
</div>
</body></span>
<span style="color:#330033;"> angular.module('myApp',[]).controller('MessageController', function($scope) {
$scope.getMessage = function() {
setTimeout(function() {
$scope.message = 'Fetched after 3 seconds';
console.log('message:'+$scope.message);
}, 2000);
}
$scope.getMessage();
}); </span>
通过运行这个例子,你会看到过了两秒钟之后,控制台确实会显示出已经更新的model,然而,view并没有更新。原因也许你已经知道了,就是我们忘了调用$apply()方法。因此,我们需要修改getMessage(),如下所示:
<span style="color:#330033;">angular.module('myApp',[]).controller('MessageController', function($scope) {
$scope.getMessage = function() {
setTimeout(function() {
$scope.$apply(function() {
//wrapped this within $apply
$scope.message = 'Fetched after 3 seconds';
console.log('message:' + $scope.message);
});
}, 2000);
}
$scope.getMessage();
}); </span>
$digest循环会运行多少次?
当一个$digest循环运行时,watchers会被执行来检查scope中的models是否发生了变化。如果发生了变化,那么相应的listener函数就会被执行。这涉及到一个重要的问题。如果listener函数本身会修改一个scope model呢?AngularJS会怎么处理这种情况?
答案是$digest循环不会只运行一次。在当前的一次循环结束后,它会再执行一次循环用来检查是否有models发生了变化。这就是脏检查(Dirty Checking),它用来处理在listener函数被执行时可能引起的model变化。因此,$digest循环会持续运行直到model不再发生变化,或者$digest循环的次数达到了10次。因此,尽可能地不要在listener函数中修改model。
service
服务是一个单例对象,在每个应用中只会被实例化一次(被$injector实例化),并且是延迟加载的(需要时才会被创建)。
注册一个服务
angular.module('myApp.services', [])
.factory('githubService', function() {
var serviceInstance = {};
// 我们的第一个服务
return serviceInstance;
});
var Person = function($http) {
this.getName = function() {
return $http({ method: 'GET', url: '/api/user'});
};
};
angular.service('personService', Person);
angular.module('myApp')
.factory('myService', function() {
return {
'username': 'auser'
};
})
// 这与上面工厂的用法等价
.provider('myService', {
$get: function() {
return {
'username': 'auser'
};
}
});
angular.module('myApp.services', [])
.factory('githubService', function($http) {
var githubUrl = 'https://api.github.com';
var runUserRequest = function(username, path) {
// 从使用JSONP调用Github API的$http服务中返回promise
return $http({
method: 'JSONP',
url: githubUrl + '/users/' +
username + '/' +
path + '?callback=JSON_CALLBACK'
});
};
// 返回带有一个events函数的服务对象
return {
events: function(username) {
return runUserRequest(username, 'events');
}
};
});
可以在控制器、指令、过滤器或另外一个服务中通过依赖声明的方式来使用服务。
将服务的名字当作参数传递给控制器函数,可以将服务注入到控制器中。当服务成为了某个控制器的依赖,就可以在控制器中调用任何定义在这个服务对象上的方法。
angular.module('myApp', ['myApp.services'])
.controller('ServiceController', function($scope, githubService) {
// 我们可以调用对象的事件函数
$scope.events = githubService.events('auser');
});
constant('name','value')
angular.module('myApp') .constant('apiKey','123123123')
//这个常量服务可以像其他服务一样被注入到配置函数中:
angular.module('myApp')
.controller('MyController', function($scope, apiKey) {
// 可以像上面一样用apiKey作为常量
// 用123123123作为字符串的值
$scope.apiKey = apiKey;
});
value('name','value')
angular.module('myApp')
.value('apiKey','123123123');
value()方法和constant()方法之间最主要的区别是,常量可以注入到配置函数中,而值不行。通常情况下,可以通过value()来注册服务对象或函数,用constant()来配置数据。
angular.module('myApp', [])
.constant('apiKey', '123123123')
.config(function(apiKey) {
// 在这里apiKey将被赋值为123123123
// 就像上面设置的那样
})
.value('FBid','231231231')
.config(function(FBid) {
// 这将抛出一个错误,未知的provider: FBid
// 因为在config函数内部无法访问这个值
});
下面我们来说说他们的区别 (provider,value,constant,service,factory,decorator)
provider可以为应用提供通用的服务,形式可以是常量,也可以是对象。
比如我们在controller里常用的$http就是AngularJS框架提供的provider
myApp.controller(‘MainController', function($scope, $http) {
$http.get(…)
}
在上面的代码里,就可以直接使用$http包好的各种功能了
//定义:
$provide.provider('age', {
start: 10,
$get: function() {
return this.start + 2;
}
});
//或
$provide.provider('age', function($filterProvider){
this.start = 10;
this.$get = function() {
return this.start + 2;
};
});
//调用:
app.controller('MainCtrl', function($scope, age) {
$scope.age = age; //12
});
provider的基本原则就是通过实现$get方法来在应用中注入单例,使用的时候拿到的age就是$get执行后的结果。 上面例子中的两种定义方法都可以
$provide.provider('myDate', {
$get: function() {
return new Date();
}
});
//可以写成
$provide.factory('myDate', function(){
return new Date();
});
//调用:
app.controller('MainCtrl', function($scope, myDate) {
$scope.myDate = myDate; //current date
});
下面我们去定义service
$provide.provider('myDate', {
$get: function() {
return new Date();
}
});
//可以写成
$provide.factory('myDate', function(){
return new Date();
});
//可以写成
$provide.service('myDate', Date);
value 和 constant ,value可以被修改 而constant 不能被修改,value 不能在 config 里面注入但是constant 可以
angularjs 与服务器的 交互
- 主要了解$http
- 熟悉$resource
- 学会使用 restangular 库 http://ngmodules.org/modules/restangular
- 理解promise
angularjs digest $apply
参考文献:
angular 权威教程
http://blog.csdn.net/dm_vincent/article/details/38705099
http://www.sitepoint.com/understanding-angulars-apply-digest/