关于AngularJS中的Scope体系:
AngularJS会在程序启动时创建一个$rootScope实例,然后再去搜索HTML DOM 树代码中
如果遇见ng- 元素,它就会为它们调用Scope.$new()方法来创建一个子 $scope,
而且每个子Scope内部都有一个$parent 属性指向其上一级Scope对象。
如此一个DOM树中会有很多范围创建指令,所以会有很多scope被创建出来。
它们都是以$rootScope为根的树形结构关系。它也是一个小型化了的DOM结构。
var WorldCtrl = function ($scope) {
$scope.population = 7000;
$scope.countries = [
{name: 'France', population: 63.1},
{name: 'United Kingdom', population: 61.8},
];
};
<ul ng-controller="WorldCtrl">
<li ng-repeat="country in countries">
{{country.name}} has population of {{country.population}}
</li>
<hr>
World's population: {{population}} millions
</ul>
这里我们创建了一个新变量country,把它暴露在$scope 里一边在页面上绑定。
但是由于重复创建会覆盖之前的,所以Angluar采取了为集合中的每一个元素都创建一个scope
新创建的Scope会完全匹配DOM树结构。
不同的DOM元素对应着不同的Scope对象,使用变量从相应的Scope中渲染模板。
上例中每个<li>元素会对应一个自己的scope,可以在它里面定义。
如此我们可以给WorldCtrl管理的scope定义一个worldsPercentage函数
$scope.worldsPercentage = function (countryPopulation) {
return (countryPopulation / $scope.population)*100;
}
我们就可以在ng-repeat指令里随便使用了,因为它们都是它的子集。
<li ng-repeat="country in countries">
{{country.name}} has population of {{country.population}},
{{worldsPercentage(country.population)}} % of the World's population
</li>
Scope的继承采用原型继承方式,当我们读取一个属性时,它会沿着继承链逐层向上寻找 。
var HelloCtrl = function ($scope) {
};
<body ng-app ng-init="name='World'">
<h1>Hello, {{name}}</h1>
<div ng-controller="HelloCtrl">
Say hello to: <input type="text" ng-model="name">
<h2>Hello, {{name}}!</h2>
</div>
</body>
我们可以使用$parent.propertyName 来显式引用父Scope中定义的变量。
<input type="text" ng-model="$parent.name">
这种做法是建立在DOM树结构不变的基础上的,但如果在<input>元素上层插入另外一个DOM
元素时,显然$parent就发生的变化,所以尽量避免这种显式的使用方法。
另外的一种解决办法是将属性绑定到一个对象,而不是直接绑定到scope的属性。
<body ng-app ng-init="thing={name:'World'}">
<h1>Hello,{{thing.name}}</h1>
<div ng-controller="HelloCtrl">
Say hello to:<input type="Text' ng-model="thing.name">
<h2>Hello,{{thing.name}}!</h2>
</div>
</body>
如此只是在应用程序范围内定义了一个对象thing,所以各个级别的Scope都能访问到。
Scope层级和事件系统:
Scope的层级体系可以用作事件总线。
AngularJS允许我们在Scope层级体系内传递命名事件和其负载。
事件可以从任何scope中被发出,分为上行$emit或者下行$broadcast在Scope层级体系中进行。
AngularJS核心服务和指令使用该事件总线来标识应用程序状态的重要变化。
比如我们可以监听从$rootScope发出的$locationChangeSuccess事件,来当浏览器中的URL发生变化采取相应的处理。
$scope.$on('$locationChangeSuccess',function(event,newUrl,oldUrl){
});
$on()方法可以在任意scope实例上调用,用于注册一个scope事件处理器。
该处理器函数将把event对象作为第一个参数,后续的参数将跟事件的负载和事件类型相关。
跟DOM事件类似,我们在一个event对象上调用 preventDefault()和stopPropagation()方法
将会阻止事件继续在scope层级体系内传播。
其中stopPropagation() 用于从下向上的$emit产生事件。
事件在scope层级体系内传递将会为许多问题带来极好的解决方案。
尤其是有关全局,异步的状态发生变化时通知某些元素。不过,这种方式现实中使用较少。
通常我们使用的双向数据绑定来解决。
在整个AngularJS框架中,只有三个事件被发射
$includeContentRequested
$includeContentLoaded
$viewContentLoaded
和七个事件被广播:
$locationChangeStart
$locationChangeSuccess
$routeUpdate
$routeChangeStart
$routeChangeSuccess
$routeChangeError
$destroy
优先使用数据双向绑定方案。
Scopes的生命周期
Scopes用于提高隔离的空间避免变量名冲突。
Scopes用于分层组织管理内存空间。
当一个 scope不再需要时就会被销毁,在其内定义的model和功能函数都将一并被垃圾收集器收回。
新的scope一般都是被scope的创建指令负责创建和销毁。
也可以通过手动调用scope的$new()和$destroy()方法来完成创建和销毁。
关于视图:
AngularJS使用HTML语言以及增强HTML元素来定义页面。
AngularJS在浏览器将标记文本转化为DOM树后切入并沿着DOM结构逐步解析。
只要遇到它的指令,AngularJS就会执行它自己的逻辑将指令转化成动态部分展示。
Angular依托于合法的DOM树,它需要跟HTML的DOM树 类似于创建一个domain-specific language(DSL)
来告诉浏览器如何处理新的增强元素。
AngularJS本身定义了一个顶级的命名空间angular,它下面定义了很多方便的属性和函数。
module() 就是用来定义一个模块空间的函数。
module 用作管理Angular其它对象的容器,它管理诸如controllers,services等)
angular.module('moduleName',[dependencyList]) 返回一个module对象。
定义完成module后,我们还需要把它放置到DOM结构中,通知AngularJS它的存在,否则它没有运行的入口。
<body ng-app="hello">
对象协作:
AnglarJS提供了module来组织管理相互协作的对象,它不仅仅可以注册框架可以直接调用的对象(controllers,filters等)
还可以注册开发人员定义的任意对象。
Module模式在我们组织应用程序代码时非常有用,AngularJS对其的利用更进一步。
出了注册对象到一个命名空间外,它还能通过声明描述这些对象之间的依赖关系。
var NotificationsService = function () {
this.MAX_LEN = 10;
this.notificationsArchive = new NotificationsArchive();
this.notifications = [];
};
NotificationsService.prototype.push = function (notification) {
var newLen, notificationToArchive;
newLen = this.notifications.unshift(notification);
if (newLen > this.MAX_LEN) {
notificationToArchive = this.notifications.pop();
this.notificationsArchive.archive(notificationToArchive);
}
};
NotificationsService.prototype.getCurrent = function () {
return this.notifications;
};
服务注册
AngularJS中有一个专门的服务$provide 用于注册不同的对象创建方法。
注册的方法会被$injector 截取器截取后提供对象实例。
$injector服务创建具体的对象,整个生命周期过程中Angular只会截取创建方法一次
创建一个单独的对象实例。
也就是所$injector创建的对象实例是单例模式的,每一个运行的应用程序只能有一个。
AngularJS的Module只是保持这些对象实例,这些对象实例的创建是由我们来控制的。
最简单的事Value方法:
var myMod = angular.module('myMod', []);
myMod.value('notificationsArchive', new NotificationsArchive());
使用该方法注册的对象,一般不会依赖其它对象。属于简单对象。
Value()注册无依赖的简单对象。
Service方法:
当我们需要的对象有依赖时,我们就不能用value方法定义了。
最简单的注册一个有其它对象依赖的对象的方法是service(),它用来注册一个构造函数。
myMod.service('notificationsService',NotificationsService);
var NotificationsService = function (notificationsArchive) {
this.notificationsArchive = notificationsArchive;
};
Service 方法注册有依赖的对象构造函数。
注意:在AngularJS世界里,service既可以是注册构造函数或者由AngularJS DI管理的任意单例对象的方法。
实际中,Service不太常用,主要是注册已有的构造函数,让AngularJS可以使用这些构造函数创建对象。
Factory()
它是另一个为对象创建注册方法的函数。
它比Service()方法更加灵活一些,因为我们可以注册任意的对象创建函数,而不一定非是构造函数。
myMod.factory('notificationsService',function(notificationsArchive){
var MAX_LEN = 10;
var notifications = [];
return {
push:function (notification) {
var notificationToArchive;
var newLen = notifications.unshift(notification);
//push method can rely on the closure scope now!
if (newLen > MAX_LEN) {
notificationToArchive = this.notifications.pop();
notificationsArchive.archive(notificationToArchive);
}
},
// other methods of the NotificationsService
};
AngularJS使用factory函数来注册返回对象,它可以是任意的JS对象,包括function对象。
该方法是AngularJS依赖系统中最为常用的获取对象的方式。
它非常灵活,可以包含非常精致的对象创建逻辑。
它是标准函数,我们可以使用private等范围限制变量,对一些内部逻辑进行封装。
Constant()
在一个模块范围内定义常量并注入到其它合作对象时,使用该方法。
myMod.constant('MAX_LEN', 10);
它在创建服务时非常有用,能够跨多应用程序共享。唯一问题时,它要求在定义时必须设置初始值。
Provider()
是上面介绍的所有方法的通用版本。
它被定义成一个函数,该函数必须返回一个包含$get属性的对象。
而$get属性是一个工厂函数,它被调用时会返回一个service实例。
我们可以把provider认为是一个嵌入了一个工厂方法的对象。
Provider方法返回一个对象,该对象可以包含额外的属性和方法,用于在$get()方法被调用前
设置配置可选项值,以及更加复杂的配置逻辑。
模块的生命周期:
AngularJS支持多种对象的创建方式。其中Provider是最为特别的,它在生产对象实例时支持更加
深入的配置设置。
为了支持provider,AngularJS 将module的生命周期划分为两个阶段。
配置阶段和运行阶段
配置阶段主要完成所有配方方法的调用和配置
运行阶段则主要是实例化后的一些逻辑执行。
myMod.config(function(notificationsServiceProvider){
notificationsServiceProvider.setMaxLen(5);
});
而运行阶段,我们可以认为是应用程序的主函数,与其它语言不同的是AngularJS
模块可以有多个配置和运行块。
一个运行的应用程序其实是一些相互协作的对象集合。
它可以有多个入口。
angular.module('upTimeApp', []).run(function($rootScope) {
$rootScope.appStarted = new Date();
});
两个阶段的不同注册方法:
Variable,Service,Factory在配置阶段不能注册
而 Constant和Provider是可以在配置阶段注册的
运行阶段能够注册的方法有Constant,Variable,Service,Factory等。
注意Provider不能在运行阶段注册。
模块之间的依赖关系:我们可以将相关的服务放到一个module里,创建可以重用的service类库。
angular.module('application', ['notifications', 'archive'])
顶层的module可以声明依赖它所需要的所有模块。
Services可以依赖其它services,values,和constants
服务和它们的跨模块可见性:
我们可以在某个字模块里定义一个service然后将它注入到父模块中使用。
angular.module('app', ['engines'])
.factory('car', function ($log, dieselEngine) {
return {
start: function() {
$log.info('Starting ' + dieselEngine.type);
};
}
});
angular.module('engines', [])
.factory('dieselEngine', function () {
return {
type: 'diesel'
};
});
兄弟模块定义的内容,相互之间也是可见的。
angular.module('app', ['engines', 'cars'])
angular.module('cars', [])
.factory('car', function ($log, dieselEngine) {
return {
start: function() {
$log.info('Starting ' + dieselEngine.type);
}
};
});
angular.module('engines', [])
.factory('dieselEngine', function () {
return {
type: 'diesel'
};
});
在一个应用程序模块中定义的服务在其它所有模块中都是可见的,也就是说模块的层级不会
影响所有定义服务的可见性。
在应用程序启动时,AngularJS会将所有模块中的服务定义都集中到一个应用程序的全局空间内。
所以,所有服务必须是单例模式的。
我们在使用依赖对象而又不想使用其某个方法定义时,可以直接重新定义该方法。
angular.module('app', ['engines', 'cars'])
.controller('AppCtrl', function ($scope, car) {
car.start();
});
angular.module('cars', [])
.factory('car', function ($log, dieselEngine) {
return {
start: function() {
$log.info('Starting ' + dieselEngine.type);
};
}
})
.factory('dieselEngine', function () {
return {
type: 'custom diesel'
};
});
JQuery作为一个类库用于简化DOM操作,比如document遍历,事件处理,动画,Ajax交互等。
AngularJS是一个完整的框架,它是以model为中心的编程模式,而Jquery则是以DOM为中心的