1 Introduction & Conceptual Overview

HTML本身是静态的,对于现在的动态网页无能为力.为了弥合静态和动态之间 的鸿沟,有两种静态方式:库(jQuery),框架(durandal, ember). AngularJS 则提供了第三种方式,通过数据绑定等方式让浏览 器学会新的语法. AngularJS 是完全基于前端的,适用于CRUD应用.

2 DI

AngularJS中的模组类似于名字空间,在几乎所有东西都在名字空间下注册.这 些注册的东西是通过名字推断来进行绑定(引用)的.AngularJS自带的那功能,都 是可以直接引用的.自己定义的东西,也可以直接靠名字推定,但是要给模组 加上依赖关系.

service, directive, filter 都是通过 factory/directive/filter 方 法注册在模组中的,这些方法统称工厂(factory)方法.

我们可以通过 config/run 方法注册在模组配置和运行的时候需要运行的 函数,这些函数也同样的可以被以类似上面的方式来调用.这些方法被归类为 模组(module)方法.

控制器(controller),被解释为classes或者constructor function,同样需 要注册到模组中,通过名字来引用.

这些东西在引用的时候,可以直接当函数参数名,可以用 $inject 来显示 说明,也可以用数组.后两种基本上是在第一种的基础上多写些代码来说明.直 接用函数参数名是最简单的,但是不被官方所推荐.

3 Data Binding & Scopes & Template

传统模板是通过模板Tempalte和模型Model来建立视图View. AngularJS是通过模 板建立起View和Model的对应,让View跟Model直接互动. AngularJS中,template是包含AngularJS元素和属性的HTML.template中可用 的AngularJS元素和属性为 Directive, Markup (), Filter, Form controls.通过这些template,JS(model)和(view)被连接了起来(数 据绑定).

上下文(Scope)提供表达式的运行环境,它被组织成一个类似DOM的层级结构.上 下文监视表达式并传递事件.API($watch)监视模型的变更,($apply)将任意 由Angular之外的事件对模型的改变传递到View.这里所谓的Angular之外的 事件,指的是controllers,services,event handlers.

上下文之间可以嵌套,嵌套的上下文有两种 child scopeisolate scope, child scope 继承上层上下文的内容, isolate scope 不继承. 如果上下文中没有对应的属性,表达式是没有任何作用的.

上下文可以看做将应用控制器controller和视图view联系起来的胶水. 在template连接的时候,directive会设置上下文中的$watch,$watch会通知 directive任何属性的变更,从而使directive能根据更新的数据来渲染DOM 控制器controller和directive都能直接访问scope,但是不能直接访问对方.这 种设计孤立controller和directive. $rootScope 提供对root上下文的访 问,一般指向ng-app所在的范围.

如果用chrome的inspect element功能查看页面元素,会发现 class="ng-scope ng-binding"之类的属 性.Anuglar会自动将 ng-scope 加入制定了上下文的元素, ng-binding 则说明这里面的内容是用绑定实现的.

在不同层级使用同一个controller,

<div ng-controller="EventController">
  Root scope <tt>MyEvent</tt> count: 
  <ul>
    <li ng-repeat="i in [1]" ng-controller="EventController">
      <button ng-click="$emit('MyEvent')">$emit('MyEvent')</button>
      <button ng-click="$broadcast('MyEvent')">$broadcast('MyEvent')</button>
      <br>
      Middle scope <tt>MyEvent</tt> count: 
      <ul>
        <li ng-repeat="item in [1, 2]" ng-controller="EventController">
          Leaf scope <tt>MyEvent</tt> count: 
        </li>
      </ul>
    </li>
  </ul>
</div>
angular.module('eventExample', [])
.controller('EventController', ['$scope', function($scope) {
  $scope.count = 0;
  $scope.$on('MyEvent', function() {
    $scope.count++;
  });
}]);

这个例子中,首先我们看JS的部分.这部分注册了一个EventController,这个 controller对于给定的上下文 $scope 设定属性 count,同时注册一个 名为 MyEvent 的事件,调用一个闭包函数对于当前上下文进行操作.

接下来看HTML,里面把同一个controller用了三次,也就意味着对于 eventExample,给予三个不同的 $scope.而这三个 $scope 各自拥有自 己的 MyEvent 函数副本.最后, $emit('MyEvent') 将调用这个函数的 动作传递给上层 $scope, $broadcast('MyEvent') 则传递给下层 $scope.

$scope 的生命周期中,模型的改变不会立即触发 $watch, 而是通过在 $apply 结束时的 $digest 来触发 $watch,这么做的好处在于将多个 改变合为一个 $watch 动作. 当子上下文不需要继续存在时,子上下文的 创建者负责调用 scope.$destroy() 负责停止事件传递,允许垃圾回收来 释放内存.

4 Module & Provider

angular.module会覆盖已存在的名字.模组提供 provider, factory, service, value, constant, animation, filter, controller, directive, config, run 这些方法.除了最后两个以 外,都是注册某一种功能. provider,factory,service,value,constant 都是JS内部用的, directive,filter,animation,controller 都是在html里面用 的(JS里面定义的).

  • JS
    • provider 提供一个在config阶段配置内容的机会,通过 $get 函数返 回绑定的对象.
    • factory 注册一个名字空间,跟value类似,但是可以引用其它的服务
    • service 注册一个类,用 new 来获得对象
    • value 注册一个变量,也可以在变量下加入函数,但是不能引用其它的服务.
  • html
    • directive, 定义一个html元素,属性,类名,对相应的元素加入定义好的模块
    • controller, 注册一个可在html里面引用的属性值,对相应元素中的变 量进行绑定和操作.
    • animation, 动作效果
  • html and JS
    • filter, 过滤器. 返回一个过滤函数并绑定在这个名字上. 过滤函数接 受整个数组为第一个参数,还可以自定附加参数.

Provider是AngularJS的基础,Value,Factory,Service,Constant都建立在Provider之上.下 面的 myApp.XXX 方法实际上都是调用 $provider.XXX.

  • Value recipe
    var myApp = angular.module('myApp', []);
    myApp.value('clientId', 'a12345654321x');
    myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
      this.clientId = clientId;
    }]);
    
    <html ng-app="myApp">
      <body ng-controller="DemoController as demo">
        Client ID: 
      </body>
    </html>
    

    其实就是定义一个模组下的变量/对象,可以是简单值,也可以在这个变量 下加入函数.

  • Factory recipe 相对Value,可使用其他服务,初始化服务,惰性初始化. 使用 myApp.factory 方法.返回的是一系列变量,函数.
  • Service recipe 使用 myApp.service() 方法,注册一个构造函数,得到一个类(class), 用 new 来得到新实例.返回的是构造函数.
  • Provider Recipe 比Factory更高一层,能够让用户在使用服务之前进行定制化,用config函 数来实现.Provider里面有个 $get 方法,DI会把这个方法返回的东西绑 定到注册的Provider名字上.
    myApp.provider('AAA', function AAAProvider(){
        this.configFunc = function(bla){blabla};
        this.$get = function(lab){ return BBB};
    });
    myApp.config(function(AAAProvider){
        AAAProvider.configFunc(...);
    });
    myApp.controller('CCC', function(AAA){ /*AAA is BBB*/});
    

    provider 只能在config里面,而在run里面,只能用非 provider.

  • Constant Recipe Constant在config和run的过程中都存在(在他们之前).用 myApp.constant 来注册.
  • Special Purpose Objects 包括controller, directive, filter, animation.

4.1 Service

服务都是惰性加载和单例,通过 factory 注册.服务本身对于html是不可 见的.在JS里面引用其功能. 除了用工厂方法意外,还可以在模组的config方法中通过 $provie.factory 来 注册服务.这里的Service和前面Provider段落中的不是一个概念.

4.2 Controller

控制器是用于连接上下文和服务的,通过 controller 注册,对于html可见,通 过 ng-controller 来进行绑定.本身也可以定义一些行为.不需要返回什 么东西.

5 Directive

Directive提供自己定义的HTML标记,基于AngularJS HTML compiler ($compile). ngApp, ngRepeat 等等就是AngularJS自带的 directive,这些directive通过 ng-app, ng-repeat 标记在HTML文件中 改变元素的行为模式.当然,这里的compile并不是真正意义上的编 译,AngularJS只是将事件监听器绑定到HTML元素上,使得这些元素可以互动. 在 $compile 的API中可以找到directive的各种option的详细说明.

命名方面, -,:,_ 都可以作为分隔符, ng-modelng:model 是等 效的.同时还可以在前面加上 x- 或者 data- 前缀, ng-modeldata-ng:model 是等效的.

使用directive可以将它作为tag name, attribute name, comment 或者 class name.

<my-dir></my-dir>
<span my-dir="exp"></span>
<!-- directive:my-dir exp-->
<span class="my-dir: exp"></span>

以上三种标记方式是等效的.当然,推荐使用tag name和attribute name.

ngAttr 能够绕过浏览器限制使用AngularJS的表达式设定attribute.

<circle cx=""></circle>
<circle ng-attr-cx=""></circle>
<svg viewBox=""></svg>
<svg ng-attr-view-box=""></svg>

Directive返回固定的option.如果说Service(Factory)是注册名字和对应的属性,函数;Directive则是将值和函数绑定到预先定义好的选项

5.1 注册Directive

module.directive 函数注册directive,它接受一个名字和一个工厂方法.方 法返回template,用其填充directive元素的内容.

5.2 restrict option

值为 ‘A’ (match attribute), ‘E’ (match element), ‘C’ (match class).指定这个directive被用作元素的属性,元素名或类

5.3 link option

操作DOM.这个option需要指定一个函数,函数的参数为 scope (上下文),element (当前元素),attrs (当前元素的属性), ctrl (array of controllers required by the directive).

angular.module('docsTimeDirective', [])
.controller('Controller', ['$scope', function($scope) {
  $scope.format = 'M/d/yy h:mm:ss a';
}])
.directive('myCurrentTime', ['$interval', 'dateFilter', function($interval, dateFilter) {

  function link(scope, element, attrs, ctrl) {
    var format,
        timeoutId;

    function updateTime() {
      element.text(dateFilter(new Date(), format));
    }

    scope.$watch(attrs.myCurrentTime, function(value) {
      format = value;
      updateTime();
    });

    element.on('$destroy', function() {
      $interval.cancel(timeoutId);
    });

    // start the UI update process; save the timeoutId for canceling
    timeoutId = $interval(function() {
      updateTime(); // update DOM
    }, 1000);
  }

  return {
    link: link
  };
}]);

设定函数 updateTime 直接操纵元素内容,通过 $watch 将元素中的属 性变动跟操作函数绑定在一起. 通过 $interval 来实现自动读秒更新时 间. 绑定 $destroy 来终止 $interval 读秒.

5.4 isolate scope

angular.module('docsIsolateScopeDirective', [])
.controller('Controller', ['$scope', function($scope) {
  $scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
  $scope.igor = { name: 'Igor', address: '123 Somewhere' };
}])
.directive('myCustomer', function() {
  return {
    restrict: 'E',
    scope: {
      customerInfo: '=info'
    },
    templateUrl: 'my-customer-iso.html'
  };
});
<div ng-controller="Controller">
  <my-customer info="naomi"></my-customer>
  <hr>
  <my-customer info="igor"></my-customer>
</div>

controller里面绑定了 naomiingor 的值,html里面将directive 元素中的 info 绑定为 naomiingor. directive里面定义独立 上下文,将 customerInfo 绑定到元素里的 info. my-customer-iso.html 里面引用 customerInfo 的内容. 如果要 引用 <div bind-to-this="thing">, 在directive里面应该用 =bindToThis.如果isolate scope里的 属性名跟directive标签中的一样,则可以缩写为 XXX: '='. 注意:如同之前提到过的,isolate scope没有继承高级上下文的内容,在这 里面调用高级上下文中的变量,只能是空 template里面或者templateUrl引用的html里面的内容继承的是directive 的上下文

5.5 transclude

给予外部上下文访问能力.同时也意味着之前isolate scope 被遮盖了,无法访问.

angular.module('docsIsoFnBindExample', [])
.controller('Controller', ['$scope', '$timeout', function($scope, $timeout) {
  $scope.name = 'Tobias';
  $scope.hideDialog = function () {
    $scope.dialogIsHidden = true;
    $timeout(function () {
      $scope.dialogIsHidden = false;
    }, 2000);
  };
}])
.directive('myDialog', function() {
  return {
    restrict: 'E',
    transclude: true,
    scope: {
      'close': '&onClose'
    },
    templateUrl: '<div class="alert"><a href class="close" ng-click="close()">&times;</a><div ng-transclude></div></div>'
  };
});
<div ng-controller="Controller">
  <my-dialog ng-hide="dialogIsHidden" on-close="hideDialog()">
    Check out the contents, !
  </my-dialog>
</div>

点击动作为ng-click="close()",而 close 动作被绑定到 &onClose 这个directive. on-close 又在 html里面绑定到 hideDialog 函数上. 这个示例中显示了如何将函数表 达式传递给directive,让directive在一定时候选择执行这个函数.scope中 读取值用’@XX’,读取表达式结果用’=XX’,读取表达式但不执行用’&XX’.

5.6 scope

不管是template还是templateUrl都是在directive的上下文下.当没有 isolate scope的时候,直接访问外部上下文.有isolate scope的时候,只能 访问isolate scope.这个时候html里面元素的内容,比如 <AA>blabla</AA> 里面的 blabla 是被忽略/覆盖的. 当在template中加入一个含有transclude的元素的时候,html里面的 blabla 会被显示在这个元素之中,而这个含有transclude的元素的内容 却不会被显示,同时html里面的表达式无法访问isolate scope.

...
return {
    template: <div>CONTENT IN JAVASCRIPT (DIRECTIVE SCOPE (OUTER/ISOLATE SCOPE))</div><div ng-transclude>CONTENT FROM HTML (OUTER SCOPE)</div>
}

5.7 controller in directive.

6 Filters

在html里面用的时候 在JS里面使用一个叫做 filter 的过滤器

angular.module('FilterInControllerModule', []).
controller('FilterController', ['filterFilter', function(filterFilter) {
  this.array = [
    {name: 'Tobias'},
    {name: 'Jeff'},
    {name: 'Brian'},
    {name: 'Igor'},
    {name: 'James'},
    {name: 'Brad'}
  ];
  this.filteredArray = filterFilter(this.array, 'a');
}]);

创建过滤器

angular.module('myReverseFilterApp', [])
.filter('reverse', function() {
  return function(input, uppercase) {
    input = input || '';
    var out = "";
    for (var i = 0; i < input.length; i++) {
      out = input.charAt(i) + out;
    }
    // conditional based on optional argument
    if (uppercase) {
      out = out.toUpperCase();
    }
    return out;
  };
})

$filter 服务通过名字调用相应的过滤器.

7 Forms

8 Expressions

9 HTML Compiler

10 Security

11 i18n and l10n

12 Accessibility

13 Bootstrap

14 Running in Production

15 Animations