AngularJS自定义指令directive()

2015-2-6    分类: angularjs笔记及案例,前端资源

angular创建自定义指令使用directive()例如:

var app = angular.module('myapp',[]);
  app.directive('helloWorld',function(){
     return {
       restrict: 'AE',  //推荐使用A
       replace: true,   //template会覆盖掉自定义标签
       template: '<h3>Hello World!</h3>' //自定义标签要显示的内容
     }
});

创建出来的便签推荐使用<div hello-world></div>

第一个参数是指令的名称

第二个参数是一个返回指令定义对象的函数。如果你的指令对额外的对象/服务(services)例如 $rootScope, $http 或者 $compile 有依赖,它们也可以在其中被注入。

 

link函数和作用域

link函数是负责操作DOM元素,例如:

app.directive('helloWorld',function(){
    return {
        restrict: 'AE',   
        replace: true,
        template: '<p></p>',   
        link: function(scope,elem,attr){
            elem.bind('click',function(){
                elem.css('background-color','white');
            scope.$apply(function(){
                scope.color = "white";
            });
            });
            elem.bind('mouseover',function(){
                elem.css('cursor','pointer');
            });
        }
    }
});   

注意到link函数被用在了指令中。它接收三个参数:

  • scope - 它代表指令被使用的作用域。在上面的例子中它等同于符控制器的作用域。
  • elem - 它代表绑定指令的元素的jQlite(jQuery的一个自己)包裹元素。如果你在AngularJS被包含之前就包括了jQuery,那么它将变成jQuery包裹元素。由于该元素已经被jQuery/jQlite包裹,我们没有必要将它包含在$()中来进行DOM操作。
  • attars - 它代表绑定指令的元素上的属性。例如,如果你在HTML元素上有一些指令形式为:<hello-world some-attribute></hello-world>,你可以在link函数内用attrs.someAttribute来引用这些属性。

link函数主要是用来对DOM元素绑定事件监听器,监视模型属性变化,并更新DOM。在前面的指令代码中,我们绑定了两个监听器,click和mouseover。click处理函数重置了

的背景颜色,而mouseover处理函数则将游标改变为pointer。模板中拥有表达式{{color}},它将随着父作用域中的模型color的变化而变化,从而改变了Hello World的背景色。

Compile函数

Compile函数主要用来在link函数运行之前进行一些DOM转化。它接收下面几个参数:

  • tElement - 指令绑定的元素
  • attrs - 元素上声明的属性

这里要注意compile不能够访问scope,而且必须返回一个link函数。但是,如果没有compile函数以依然可以配置link函数。compile函数可以被写成下面的样子:

app.directive('test',function(){
    return {
        compile: function(tElem,attrs){
            //在这里原则性的做一些DOM转换   
            return function(scope,elem,attrs){
             //这里编写link函数
            }
        }
    }
});   

什么时候使用compile?

大多数时候,你仅仅只需要编写link函数。这是因为大部分指令都只关心与注册事件监听器,监视器,更新DOM等等,它们在link函数中即可完成。像是ng-repeat这样的指令,需要多次克隆并重复DOM元素,就需要在link函数运行之前使用compile函数。你可能会问威慑呢么要将两个函数分别使用。为什么我们不能只编写一个函数?为了回答这个问题我们需要理解Angular是如何编译指令的!

 

指令是如何被编译的

当应用在启动时,Angular开始使用$compile服务解析DOM。这项服务会在标记中寻找指令然后将它们各自匹配到注册的适龄。一旦所有的指令都已经被识别完成,Angular就开始执行它们的compile函数。正如前面所提到的,compile函数返回一个link函数,该函数会被添加到稍后执行的link函数队列中。这叫做编译阶段(compile phase)。注意到即使同一个指令有几个实例存在,compile函数也只会运行一次。

在编译阶段之后就到了链接阶段(link phase),这时link函数就一个接一个的执行。在这个阶段中模板被生成,指令被运用到正确的作用域,DOM元素上开始有了事件监听器。不像是compile函数,lin函数会对每个指令的实例都执行一次。

改变指令的作用域

默认情况下指令应该访问父作用域。但是我们并不像对所有情况一概而论。如果我们对指令暴露了父控制器的scope,那么指令就可以自由的修改scope属性。在一些情况下你的指令可能想要添加一些只有内部可以使用的属性和函数。如果我们都在父作用域中完成,可能会污染了父作用域。因此,我们有两种选择:

  • 一个子作用域 - 这个作用域会原型继承父作用域。
  • 一个隔离的作用域 - 一个全新的、不继承、独立存在的作用域。

作用域可以由指令定义对象中的scope属性定义。下面的例子展示了这一点:

app.directive('helloWorld',function(){
    return {
        scope: true, //使用一个继承父作用域的自作用域   
        restrict: 'AE',
        replace: true,
        template: '<h3>Hello World!</h3>'
    }
});   

上面的代码要求Angular为指令提供一个能够原型继承父作用域的子组用于。另一种情形,一个隔离作用域,代码如下所示:

app.directive('helloWorld',function(){
    return {
        scope: {}, //使用一个全新的隔离作用域   
        restrict: 'AE',
        replace: true,
        template: '<h3>Hello World!</h3>'
    }
});

上面的指令使用一个不继承父作用域的全新隔离作用域。当你想要创建一个可重用的组件时隔离作用域是一个很好的选择。通过隔离作用域我们确保指令是自包含的兵可以轻松地插入到任何HTML app中。这种做法防止了父作用域被污染,由于它不可访问父作用域。在我们修改后的helloWorld指令中如果你将scope设置为{},那么代码就不会再正常运行。它将创建一个隔离的作用域然后表达式{{color}}将无法引用隔离作用域中的属性因此值变为undefined。

隔离作用域并不意味着你一点都不能获取到父作用域中的属性。有一些技巧可以使你访问父作用域中的属性同时监听这些属性的变化。我们将在下一篇文章中提到这种高级技巧。