Angular 不完全参考手册

Angulare及Ember之后又一前端MVC的一大力作,同时也越来越多的应用到现在Web应用开发当中,这里我们来了解一下Angular中非常重要的一块Derective

用简单的方式理解Directive就是通过标记(例如通过 属性, 标签, 或者css的 class)特定的DOM元素,为其提供额外功能的方法。

angular.module("myApp")
  .directive("myDirective", function(){

    // directive define here

  });

申明一个指令需要两个元素:

name(string)

Directive的名称

factory_function(function)

工厂方法需要返回一个用于定义指令行为的对象。

angular.module("myApp")
  .directive("myDirective", function(){

    // directive define here
    return {
      // directive definition options
    };

  });

当Angular启动以后,Angular的compiler将会解析document通过匹配标签,属性,或者css类需找directive,当找到相应的元素,则通过我们定义的指令方法作用于标签之上。

实例见:http://jsbin.com/kovak/7/

Directive参数详解

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: return an Object OR
            function(tElement, tAttrs, transclude) {
            return {
                pre: function(scope, iElement, iAttrs, controller) { ... },
                post: function(scope, iElement, iAttrs, controller) { ... }
            }
            // or
            return function postLink(...) { ... }
        }
    };
});

restrict(string)

可选参数,定义将使用哪种方式(element, attribute, class, comment)在DOM中申明一个指令,默认情况下Angular使用attribute申明directive, 申明的可选值

  • E 作为元素
  • A 作为属性
  • C 作为类
  • M 作为注释

example:


example:

angular.module('myApp', []);
angular.module("myApp")
  .directive("myDirective", function(){
    // directive define here
    return {
      // directive definition options
      restrict: 'AE'
    };
});

在页面中通过如下方式调用:

<-- as an attribute -->
<-- or as an element -->

注意事项,请慎用Element的定义方式. IE你懂得~

priority(numver)

可选参数,默认值0. 该属性定义了指令的优先级,当同一个dom元素定义了多个指令,优先级高的先执行,一般情况下大部分指令是忽略了这个值得,只有特殊的如ng-repeat设置为1000,如此ng-repeat总能在其它指令之前先运行,而保证不出先一些怪异的情况

terminal(boolean)

可选参数,默认值。 当定义了该参数后,diretive将会告诉Angular,停止执行之后的有更高priority的指令,而不影响其他相同优先级的directive。

注意,在定义的terminal参数的指令元素上,加入优先级更高的指令时,优先级更高的指令将不会执行。

template(string|function)

可选参数。directive的模版,必须是以下类型之一

  • string: HTML的字符串表示
  • function(tElement, tAttrs): 一个函数并且返回值必须为一个HTML的字符串,这里的tElement, tAttrs是正对模版的,不同于申明了指令的dom元素。

example:

template: '

When using two elements, wrap them in a parent element

'

对angular而言,处理模版和处理其他的html是一样的,模版中我们同样可以使用angular的标记如

templateUrl(string|function)

可选参数,如果提供必须是一下类型之一:

  • string: 指向html模版的路径
  • function(tElement, tAttrs):返回html模版的路径

默认情况下,当directive被调用时,将使用ajax请求相应的模版文件

注意1,在本地开发时为了防止CORS错误,应该使用静态资源服务器WebStorm自带了一个build-in server在开发时会方便

注意2:模版文件是同步加载的,这意味着compilation和link函数会在模版加载完成之后才开始运行

大量的同步加载模版将会使客户端速度和体验明显下降,这时我们应该考虑缓存模版,这时候你应该考虑使用$templateCache

replace(boolean)

可选参数,默认值为false; 默认情况下指令的模版将作为子元素添加到申明了指令的Dom元素之中

js:

angular.module('myApp', []);

angular.module("myApp")
  .directive("myDirective", function(){

    // directive define here
    return {
      restrict: 'EA',
      // directive definition options
      template: '
some stuff here
' }; });

html:

生成的html如下所示:

some stuff here

当replace为true时,生成的html如下所示

some stuff here

指令的Sope(域)

为了更好的理解,剩下的参数,我们有必要了解一下scope是如何工作的。

当在DOM中申明ng-app指令时,一个特殊的对象$rootscope将会被创建

Inside Div Two

如上代码所示,我们在rootsocpe上创建了3个属性:someProperty, siblingProperty, aThirdProperty。

从这里开始,每一个在DOM中的directive将会:

  • 直接使用这些属性
  • 创建一个新的对象并且集成rootscope对象
  • 创建一个完全独立与rootscope的对象

实例展示的是第一种情况,第二个div是一个兄弟节点它可以直接set和get$rootScope. 此外,在第二个div中的div同样有访问同一个rootScope的权利。 默认情况下子指令具有访问它们父节点的scope的的权限。

而指令也仅仅是作为内嵌在其他指令下,并不意味着scope对象被改变了。到这里也就可以理解为什么申明指令时scope参数是默认为false了。

提示:关于scope集成的问题可以参考Javascript的原型集成模型

scope(boolean|object)

可选参数,默认值False. 可以设置为true或者一个object {}.

当设置为true时,指令的scope将会原型集成自父节点scope

内置的 ng-controller指令存在的目的就在于创建一个原型继承自父scope的scope.下面是一个例子:

html:

Inside Div Two: {{ aThirdProperty }}
Inside Div Three: {{ aThirdProperty }}
Inside Div Four: {{ aThirdProperty }}

js:

angular.module('myApp', []);

angular.module('myApp')
  .controller('SomeController', function($scope){

  });

result:

Inside Div Two:
Inside Div Three: data for 3rd property
Inside Div Four: data for 3rd property

如上所示,ng-controller的socpe集成自rootScope. 在对ng-controller的scope添加属性后,不会影响rootScope的属性

再次提醒:JavaScript的原型集成模型

html:

Inside Div Two: {{ aThirdProperty }}
Inside Div Three: {{ aThirdProperty }}
Inside Div Four: {{ aThirdProperty }}
Outside myDirective: {{ myProperty }}
Inside myDirective: {{ myProperty }}

js:如下所示我们定义了一个SecondController,定义了一个新的指令myDirective并且设置scope为true,这意味着myDirective的域将会原型集成自父节点的域。

angular.module('myApp', []);

angular.module('myApp')
  .controller('SomeController', function($scope){

  })
  .controller('SecondController', function($scope){

  })
  .directive('myDirective', function() {
    return {
        restrict: 'A',
        scope: true
    };
});

result:

Inside Div Two:
Inside Div Three: data for 3rd property
Inside Div Four: data for 3rd property
Outside myDirective:
Inside myDirective: wow, this is cool

完整代码请点击
http://jsbin.com/lehan/31/希望上面的例子能对你理解socpe是如何工作的有所帮助。

隔离Scope

隔离的scope可能是最容易让人疑惑,同时也是最强大的工具之一,最主要的用例是创建可重用的组件。 避免不经意间污染其他的父scope对象。

为了创建一个具有隔离Scope的指令,我们需要设置指令的scope属性为一个空对象{},如此设置之后指令的模版将不能使用任何父scope的属性。

html:

Outside myDirective: {{ myProperty }}
Inside myDirective: {{ myProperty }}

js:

angular.module('myApp', [])
    .controller('MainController', function($scope) {})
    .directive('myDirective', function() {
      return {
        restrict: 'A',
        scope: {},
        template: '
Inside myDirective {{ myProperty }}
' }; });

result:

Outside myDirective: hello world
Inside myDirective

完整代码参考http://jsbin.com/gisoz/61/

Transclude

可选参数,默认值false;如果提供必须设置为true.
Transclue经常被用来创建可重用的组件,一个很好的例子就是如modal box以及导航条。

html:

  • First link
  • Second link

js:

angular.module('myApp', [])
  .directive('sidebox', function() {
    return {
      restrict: 'EA',
      scope: {
        title: '@'
      },
      transclude: true,
      template: ''
    };
});

上面的代码将会告诉Angular的编译器寻找模版中的ng-transclude指令,并将DOM内的元素作为子元素添加到ng-transclude标签下。

上述代码生成的Html结果如下所示:

完整代码参考http://jsbin.com/gisoz/73/

controller(string|function)

可选参数,可以设置为:

  • string: Angular将会根据名字找到相应的已经注册的Controller;

    angular.module('myApp', [])
    .directive('myDirective', function() {
        restrict: 'A', // always required
        controller: 'SomeController'
    });
    
    angular.module('myApp')
        .controller('SomeController', function($scope, $element, $attrs, $transclude) {
            // controller logic goes here
    });
    
  • function($scope, $element, $attrs, $transclude): 定义内联的controller

angular.module('myApp', [])
.directive('myDirective', function() {
    restrict: 'A',
    controller:
    function($scope, $element, $attrs, $transclude) {
        // controller logic goes here
    }
});

一般情况下link 和controller是可以互换的,而使用controller的主要用例是希望controller可以在多个directive中重复使用。

controllerAs(string)

可选参数,该参数允许我们为controller设置别名

angular.module('myApp')
  .directive('myDirective', function() {
    return {
      restrict: 'A',
      template: '

{{ myController.msg }}

', controllerAs: 'myController', controller: function() { this.msg = "Hello World"; } }; });

require(string|array)

可选参数,可以设置为一个string或者一个string数组,这些string代表了当前指令所依赖的其他指令