angularjs的directive的属性含义详解

 在介绍directive之前,我想先讲讲MVC这个框架的相关知识。这样可以更好的理解angular。

什么是MVC?mvc是一种设计模式,它把应用划分为三个部分,数据(模型),展现层(视图),控制交互层(控制器),一个时间的发生时这样的过程:

1.用户和应用产生交互

2.控制器的事件处理器被触发

3.控制器从模型中请求数据,并将其交给视图

4.视图将数据呈现给用户。

模型

模型用来存放应用的所有数据对象,模型不必知晓视图和控制器的细节,模型只需包含数据及直接和这些数据相关的逻辑。任何事件处理代码、视图模板,以及那些和模型无关的逻辑都应当隔离在模型之外。将模型和视图的代码混在一起,是违反MVC架构原则的。模型是最应该从你的应用中解耦出来的部分。

当控制器从服务端抓取数据或者创建新的记录时,它就将数据包装成模型实例,也就是说,我们的数据是面向对象的,任何定义在这个数据模型上的函数或者逻辑都可以直接被调用。

因此,不要这样做:  

var user = users["foo"];
destroyUser(user);

  上面的代码没有命名空间的概念,并且不是面向对象的。如果在应用中定义了另一个destoryUser()函数的话,两个函数就会产生冲突。我们应当确保全局变量和函数的个数尽可能少.而要这样做。

var user = User.find("foo");
user.destroy();

  上面的代码中,destory()函数是存放在命名空间User的实例中的。这种代码更加清晰,而且非常容易做继承,类似destory()的这种函数就不用在每个模型中都定义一遍了。

视图

视图层是呈现给用户的,用户与之产生交互,在JavaScript 应用中,视图大都是由HTML、CSS和JavaScript模板组成的。除了模板中简单的条件语句之外,视图不应当包含任何其他逻辑。

这并不是说MVC不允许包含视觉呈现相关的逻辑,只要这部分逻辑没有定义在视图之内即可。我们将视觉呈现逻辑归类为“视图助手”(helper):和视图有关的独立的小型工具函数。

反例——formatDate()函数直接插入视图:

// template.html
<div>
    <script>
        function formatDate(date) {
            /* ... */
        };
    </script>

    ${ formatDate(this.date) }
</div>

  应该这样做——所有视觉呈现逻辑都包含在helper变量中,这是一个命名空间,可以防止冲突并保持代码清晰、可扩展:

  

// helper.js
var helper = {};
helper.formatDate = function(){ /* ... */ };

// template.html
<div>
    ${ helper.formatDate(this.date) }
</div>

  控制器

控制器是模型和视图之间的纽带,控制器从视图获得时间和输入,对它们惊醒处理(很可能包含模型),并相应的跟新视图。当页面加载工作时,控制器会给视图添加事件监听,比如监听表单提交或者按钮点击。然后,当用户和应用产生交互时,控制器中的事件触发器就开始工作了。

下面用jQuery实现一个例子:

var Controller = {};

// 使用匿名函数来封装一个作用域
(Controller.users = function($){

    var nameClick = function(){
        /* ... */
    };

    // 在页面加载时绑定事件监听
    $(function(){
        $("#view .name").click(nameClick);
    });
})(jQuery);

  上面的代码创建了users控制器,这个控制器是放在Controller变量下的命名空间。然后用了一个匿名函数封装了一个作用域,以避免对全局作用域造成污染。当页面加载时,程序给视图元素绑定了点击事件的监听。

上面就是mvc的简单介绍。下面继续directive的属性讲解。

基本上每个directive都会经过$compile编译,然后通过link函数来拓展相应的DOM元素。下面介绍常用的directive定义的对象:

priority

当有多个directive定义在同一个DOM元素上时,有必要指定directive的应用顺序。priority对象通常用数字表示,数字越大的,相应的指定便优先编译,相反该directive对应的link函数便越靠后执行。priority的值默认为是0.

 scope(讲到这个,值得提一提$scope对象,还有在controller中使用$scope和this(当在html中使用controller as时可以使用)的区别,感兴趣的话自行搜索)

它的值有三种;true,false(默认),object(对象)。

什么是angular中的scope?

scope(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope)是一个指向model的object。也是表达式的的执行上下文(请自行了解执行上下文的概念)。angular中提供了一些常用API:$watch API(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope#$watch),用于监测model的变化,$apply API(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope#$apply),用于监测angular 定义的对象内外的所有model的变化。

在angular中,子作用域一般是通过原型继承机制继承其父作用域的属性和方法,但是有一个例外:在directive中,使用scope:{...},这种方式创建的作用域是一个独立的(isolate)作用域,它也有父作用域,但父作用域不在其原型链上,不会对父作用域原型继承,这种方式定义作用域通常用于构造可复用的directive组件,因为这样定义的scope,不会直接访问或者时候修改父作用域的属性,不会产生意外的副作用。

如果我们在子作用域中访问一个父作用域中定义的属性,那么程序会首先在子作用域中寻找该属性,没找到在从原型链上的父作用域中寻找,在没找到,再往上一级原型链找。在angular中,作用域原型链的顶端是$rootScope。

在单独的directive中,scope的概念还是比较清晰的。

当scope取值为false时,此时directive没有独立的scope对象,link函数中引用的scope对象为来自于当前节点的默认controller。

当scope取值为true是时,directive拥有独立的scope对象,此scope是由父scope继承而来,可以访问父scope中的所有属性,此时通过javasript原型继承。值得注意的是:当给此scope继承而来的属性名称赋值时,子scope会相应建立一个本地属性,此时改变的是本scope的变量属性,父scope中的属性是不会改变的。

当scope取值为{propertyName:"=@propertyValue},此时directive拥有一个隔离的scope对象,其实就是一个全新的scope对象,和上面取值的区别就是不能通过原型继承访问父scope中的属性,但是可以通过$parent属性去访问父scope中对象属性的。

下面讲讲当scope取值为{...}时,申明scope对象的引用修饰符的用法:(http://stackoverflow.com/questions/14050195/angularjs-what-is-the-difference-between-and-in-directive-scope)

1.=或者=attr  隔离作用域的属性与父作用域的属性进行双向绑定,任何一方修改都会影响对方,此时指令中的属性取值为controller中对应的$scope上属性的值,这是最常用的方式;

2.@或者@attr  此时指令中的属性取值为html中的字面量或者直接量。这样是建立一个local scope property到DOM的property的绑定,因为值总是string类型,故这个值总是返回一个字符串,并且字符串的值永远是从父作用域继承而来的(即只能读取父作用域中属性值,不能修改,属于单向绑定)。如果没有通过@attr指定属性名称,那么本地名称将于DOM的属性一致。例如<widget my-attr=”hello {{name}}”>,widget的scope定义为:{localName:’@myAttr’}。那么,widget scope property的localName会映射出”hello {{name}}"转换后的真实值。name属性值改变后,widget scope的localName属性也会相应地改变(仅仅单向,与上面的”=”不同)。name属性是在父scope读取的(不是组件scope).

 3. & or &attr “Isolate”作用域把父作用域的属性包装成一个函数,从而以函数的方式读写父作用域的属性,包装方法是$parse();

 

 controller

这是嵌套directive之间交互的重要属性对象。

 套用一个经典定义( what the O'Reily AngularJS book by the Google Team has to say):Controller - Create a controller which publishes an API for communicating across directives. A good example is Directive to Directive Communication。意即这个controller是用来存放一些可以在各个directive之间的共享的方法。当两个或更多的directive之间需要通信时(即directive A需要用到directive B中的方法M),这时方法M就可以在directive B中的controller对象中来定义这个方法。此时方法M便类似一些公共的API(可以供其他的directive使用。)详情请参考:http://www.cnblogs.com/xing901022/p/4290411.html

 这个controller可以注入以下本地变量:

$scope (当前元素作用域),$element(当前元素),$attrs(当前元素的属性对象),$transclude(后面介绍这个变量,感兴趣的自己去谷歌)。

require

 这个对象表示需要另外一个directive B(directive之间的通信)并且将会注入directive B所在的controller到linking function中,它的值一般是'xxxController'或者'^xxxController',表示这个directive需要使用directive B的controller属性中的API。

restrict

限制directive在html中作用的方式:

'E' element name,表示以元素的形式在html中作用,例如<my-directive></my-directive>

'A' attribute ,表示以属性的方式作用在html中,例如 <div my-directive='exp'></div>

'C' class ,表示以class的形式在html中作用,例如<div class='my-directive : exp ;'></div>

'M' 以注释的方式在html中作用,例如<!-- directive: my-directive exp -->

当然,以上也可以组合使用,表示逻辑and。例如 restrict : 'EA',\

template 

模板代替directive的元素的内容(默认),也可以完全代替元素本身(当replace值为true时有效),也可以用来封装directive的元素的内容(当transclude的值为true时有效)。

取值:

  • A string. For example <div red-on-hover>{{delete_str}}</div>.
  • A function which takes two arguments tElement and tAttrs (described in the compile function api below) and returns a string value.

templateUrl

模板加载地址,异步加载。

replace

取值为true时,模板将会取代directive的元素;取值为false,模板将会取代directive的元素的内容。

transclude

一般情况下取值有三种true,'element',{...},通常用到前两种。

当取值为true时, transclude the content (i.e. the child nodes) of the directive's element.举个例子:

比如说你有一个申明transclude :true的directive叫做my-transclude-true,如下:

<div>
  <my-transclude-true>
    <span>{{ something }}</span>
    {{ otherThing }}
  </my-transclude-true>
</div>

  当它被编译之后,就会变成如下:

<div>
  <my-transclude-true>
    <!-- transcluded -->
  </my-transclude-true>
</div>

 my-transclude-true这个directive的内容content(子节点),即'<span>{{something..',将在这个directive中 可用。

再比如说你有一个申明transclude :'element'的directive叫做my-transclude-element,如下:

<div>
  <my-transclude-element>
    <span>{{ something }}</span>
    {{ otherThing }}
  </my-transclude-element>
</div>

  被编译之后,就会变成如下:

<div>
   <!-- transcluded -->
</div>

  这里,它的整个元素包括它的子节点都将在这个directive中可用。当transclude这个属性被申明为'element'时,directive中的template属性将会失去作用。

最后一个重要的属性:link

这个属性只有在compile这个属性未定义时才能使用。

一般的语法糖:link : function (scope,iAttrs,iElement,controller){...}

link function的作用:Programmatically modify resulting DOM element instances, add event listeners, and set up data binding.以编程方式修改生成的DOM元素实例,添加事件监听器,设置数据绑定。这是整个directive逻辑放的最多的地方。

下面说说这个函数的参数含义:

scope  directive用来注册监听事件registering watches的作用域;

iElement  调用这个directive的实例的元素,比如:angular.module('app',[]).directive('myDirective',function(...){template :'<div data='data'><ul></ul></div>',replace:true});这个directive的元素就是'<div><ul></ul></div>',只有在postlink函数中处理元素的子节点'<ul></ul>'才是安全的,因为子节点已经被link了。(https://docs.angularjs.org/api/ng/service/$compile #link)

iAttrs 调用这个directive的实例的元素的属性,如上为[data],是一个属性列表集合。

controller:这个diective所需要的controller实例,通常在require这个属性后面已经写明。

以上大概就是平时开发过程中常用到的几个属性。

后面要续写的内容预知:

1.嵌套directive之间的通讯(即directive之间的交互);

2.directive与controller之间的数据传递和通信;

3.controller与controller之间的数据传递和通信。

posted @ 2016-04-19 16:18  xiaoxiao彭  阅读(7393)  评论(0编辑  收藏  举报