Angularjs 指令

AngularJS的启动引导过程
1. 浏览器解析DOM,DOMContentLoaded(ready)完成,Angular启动,发现ng-app,开始初始化
2. 调用angular对象上的injector()方法创建注入器$injector,指定载入的模块(controller)
3. 创建根作用域$rootScope,绑定到ng-app的DOM节点
4. 启动$compile服务编译DOM子树,编译分三步

  • 匹配指令
    $compile遍历DOM树,如果发现有元素匹配了某个指令,那么这个指令将被加入 该DOM元素的指令列表中。一个DOM元素可能匹配多个指令。
  • 执行指令的编译函数
    当一个DOM元素的所有指令都找齐后,编译器根据指令的优先级/priority指令进行排序。 每个指令的compile函数被依次执行。每个compile执行的结果产生一个link函数,这些 link函数合并成一个复合link函数。
  • 执行生成的链接函数
    $compile通过执行指令的link函数,将模板和scope链接起来。结果就是一个DOM视图和scope对象模型 之间的动态数据绑定。

为何将编译和链接两个步骤分开?

简单说,当数据模型的变化会导致DOM结构变化时,指令就需要分别定义compile()函数和link函数。 例如,ng-repeat指令需要为数据集合中的每个成员复制DOM元素。将编译和链接过程分开可以有效地提高性能,因为DOM的复制放在compile()里,仅需要执行一次,但链接则发生在每个生成的DOM元素 上,所以指令的link()函数会执行多次。

编译过程通常借助于指令,完成这几种操作:
- 对DOM对象进行变换。
- 在DOM对象上挂接事件监听。
- 在DOM对象对应的scope对象上挂接数据监听。

指令很少需要compile函数,因为大多数指令考虑的是作用于特定的DOM元素实例,而不是改变DOM 的结构。所以link函数更常用。

指令

笼统地说,指令是DOM元素(例如属性、元素、CSS类等)上的标记符,用来告诉AngularJS的HTML编译器 ($compile服务)将特定的行为绑定到DOM元素,或者改变DOM元素。

指令可以放置在元素名、属性、CSS类名称及备注中。下面是一些等效的触发”ng-bind”指令的写法:

<span ng-bind="exp"></span>
<span class="ng-bind: exp;"></span>
<ng-bind></ng-bind>

指令的实现本质上就是一个类工厂,它返回一个指令定义对象,编译器根据这个指令定义对象进行操作。指令自身会注册事件监听器$watch,因此当事件被触发时,指令函数就会运行在AngularJS的$digest循环中。定义指令名用驼峰法, 写入到DOM元素中用短线写法。

这里写图片描述

参考链接:http://xc.hubwiz.com/course/54f3ba65e564e50cfccbad4b

scope在Angular中代表着应用模型,它是模板中表达式的上下文。在scope中,你可以watch(监听)表达式值的变化,可以传播事件。 在编写控制器时,我们往往会注入一个$scope Service来设置当前模板的上下文. 在scope中,有时我们希望监听某个表达式的变化。在Angular的scope中,监听表达式的值就像注册事件处理函数一样简单:

$scope.$watch('username', function(newValue, oldValue){
    console.log('username changed:', oldValue, '->', newValue);
});

拥有ng-app属性的HTML元素会成为Angular模板,在页面载入时Angular会对它(以及它的子元素)进行编译(递归地匹配directive、controller并绑定DOM事件)。 主要有两个过程(以<input>和ng-model为例):
- input Directive找到声明了ng-model属性的<input>,绑定<input>的keydown事件。
- 在input Directive的上下文($scope)中添加对应数据模型的$watch函数,当模型改变时操作并更新DOM。
input是Angular内置的一个Directive,它会匹配<input>元素,并对它实现双向的数据绑定。Angular对几乎所有输入型控件都编写了Directive。

例如用户在输入框按下键x,浏览器触发了keydown事件。
- inputDirective中的事件处理函数被调用,该处理函数中会执行$apply(“name = ‘x’”)。
- Angular 在当前\$scope中执行(\$eval)表达式name = ‘x’,改变数据模型。
- Angular 开始\$digest循环。
- \$digest会调用所有监听该模型的\$watch listener,这些监听函数会更新各自负责的DOM。
- 浏览器重新渲染DOM。

自定义指令的配置参数

myModule.directive('namespaceDirectiveName', function factory(injectables) {

        var directiveDefinitionObject = {

            restrict: string,//指令的使用方式,包括标签E,属性A,类C,注释M

            priority: number,//指令执行的优先级

            template: string,//指令使用的模板,用HTML字符串的形式表示

            templateUrl: string,//从指定的url地址加载模板

            replace: bool,//是否用模板替换当前元素,若为false,则append在当前元素上

            transclude: bool,//是否将当前元素的内容转移到模板中(<ng-transclude></ng-transclude>)

            scope: bool or object,//指定指令的作用域 创建新作用域继承父作用域true 共用父作用域false {str : '@string',name : '=username',getName : '&getUserName'}创建独立的作用域 str和name是模板中双括号中的变量名 string是特定字符串 username是父作用域的属性 getUserName是父作用域中的函数

            controller: function controllerConstructor($scope, $element, $attrs, $transclude){this.getName=...},//定义与其他指令进行交互的接口函数,构造函数,this.XXX=..

            require: string,//指定需要依赖的其他指令 '^?anotherDirective' ^前缀表示从父节点查找,?表示失败后不抛出异常, 可以在下面的link函数中引用依赖指令的controller中的变量和函数

            link: function postLink(scope, iElement, iAttrs,otherControllor) {...},//以编程的方式操作DOM,包括添加监听器等

            compile: function compile(tElement, tAttrs, transclude){

                return: {

                    pre: function preLink(scope, iElement, iAttrs, controller){...},

                    post: function postLink(scope, iElement, iAttrs, controller){...}

                }

            }//编程的方式修改DOM模板的副本,可以返回链接函数

        };

        return directiveDefinitionObject;

});

可如下定义模板 templateUrl:'helloTemplate.html'

<script type="text/ng-template" id="helloTemplate.html">
         <div>hello</div>
</script>

关于$compile和$link的比较,举个例子

<ul>
  <li ng-repeat="a in array">
    <input ng-modle=”a.m” />
  </li>
</ul>

我们的观察目标是ng-repeat指令。假设一个前提是不存在link。$compile在编译这段代码时,会查找到ng-repeat,然后执行它的compile函数,compile函数根据array的长度复制出n个<li>标签。而复制出的<li>节点中还有<input>节点并且使用了ng-modle指令,所以compile还要扫描它并匹配指令,然后绑定监听器。每次循环都做如此多的工作。而更加糟糕的一点是,我们会在程序中向array中添加元素,此时页面上会实时更新DOM,每次有新元素进来,compile函数都把上面的步骤再走一遍,岂不是要累死了,这样性能必然不行。现在扔掉那个假设,在编译的时候compile就只管生成DOM的事,碰到需要绑定监听器的地方先存着,有几个存几个,最后把它们汇总成一个link函数,然后一并执行。这样就轻松多了,compile只需要执行一次,性能自然提升。

如果指令只进行DOM的修改,不进行数据绑定,那么配置在compile函数中,如果指令要进行数据绑定,那么配置在link函数中。

function compile(element, attrs, transclude) { ... }
在compile阶段要执行的函数,返回的function就是link时要执行的function, 常用参数为element和attrs, 分别是dom元素和元素上的属性们,较少使用,因为大部分directive是处理dom元素的行为绑定,而不是改变它们, 以下情况使用compile
- 想在dom渲染前对它进行变形,并且不需要scope参数
- 想在所有相同directive里共享某些方法,这时应该定义在compile里,性能会比较好
- 返回值就是link的function,这时就是共同使用的时候

function link(scope, element, attrs, controller) { ... }
在link阶段要执行的函数,这个属性只有当compile属性没有设置时才生效, 常用参数为scope,element和attrs,分别是当前元素所在的scope,dom元素和元素上的属性们,directive基本上都会有此函数,可以注册事件,并与scope相绑,以下情况使用link
- 对特定的元素注册事件
- 需要用到scope参数来实现dom元素的一些行为

http://www.cnblogs.com/lvdabao/p/3407424.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值