AngularJs -- 指令详解

如何定义指令:

angular.module('myApp', []) 
.directive('myDirective', function() { 
    return { 
        restrict: 'A', 
        replace: true, 
        template: '<a href="http://google.com">Click me to go to Google</a>' 
    } 
}); 

注意,我们在模板中硬编码了URL和链接文本:

template:'<a href="http://google.com"> Click me to go to Google</a>'

AngularJS并没有限制在指令的模板中硬编码字符串。

实际上,应该将上面的模板转换成可以接受两个变量的形式:一个变量是URL,另一个是链接文本:

template: '<a href="{{ myUrl }}">{{ myLinkText }}</a>'

在主HTML文档中,可以给指令添加myUrl和myLinkText两个属性,这两个参数会成为指令内部作用域的属性:

<div my-directive 
    my-url="http://google.com" 
    my-link-text="Click me to go to Google"> 
</div> 

有好几种途径可以设置指令内部作用域中属性的值。最简单的方法就是使用由所属控制器提供的已经存在的作用域。

尽管简单,共享状态会导致很多其他问题。如果控制器被移除,或者在控制器的作用域中也定义了一个叫myUrl的属性,我们就被迫要修改代码,这是成本很高且让人沮丧的事情。AngularJS
允许通过创建新的子作用域或者隔离作用域来解决这个常见问题。

同之前在当前作用域介绍中介绍的继承作用域(子作用域)不同,隔离作用域同当前DOM的作用域是完全分隔开的。为了给这个新的对象设置属性,我们需要显式地通过属性传递数据,同在
JavaScript或Ruby中给方法传递参数类似。

当用如下代码将指令的作用域设置成一个只包含它自己的属性的干净对象时:

scope: { 
    someProperty: "needs to be set" 
} 

实际上创造的是隔离作用域。本质上,意味着指令有了一个属于自己的$scope对象,这个对象只能在指令的方法中或指令的模板字符串中使用:

template: '<div>\ 
    we have access to {{ someProperty }}\ 
    </div>', 
controller: function($scope) { 
    //指令可以有它自己的控制器, 
    // 那种情况下我们可以
    // => 错误!!! 
    $scope.someProperty === "needs to be set"; 
} 
错误?

目前为止,我们一直忽略了一个细节。实际上不能像上面的例子那样,在作用域对象内部直接设置someProperty属性。

scope: { 
    // 这样行不通
    someProperty: 'needs to be set' 
} 

实际上要在DOM中像之前提到过的那样,像给函数传递参数一样,通过属性来设置值:

<div my-directive 
    some-property="someProperty with @ binding"> 
</div> 

现在,我们在作用域对象内部把someProperty值设置为@这个绑定策略。这个绑定策略告诉AngularJS将DOM中some-property属性的值复制给新作用域对象中的someProperty属性:

scope: { 
    someProperty: '@' 
} 

注意,默认情况下someProperty在DOM中的映射是some-property属性。如果我们想显式指定绑定的属性名,可以用如下方式:

scope: { 
    someProperty: '@someAttr' 
} 

在这个例子中,被绑定的属性名是some-attr而不是some-property。

<div my-directive 
    some-attr="someProperty with @ binding"> 
</div> 

现在,当我们在指令模板或控制器中(之前的例子这样做过)访问someProperty时,会得到DOM属性中的值的副本:

template:'<div>\ 
we have access to {{ someProperty }}\ 
    </div>', 
    controller: function($scope) { 
    // 指令可以有它自己的控制器,在那种情况下,我们可以将
    // $scope.someProperty设置成
"someProperty with @ binding" 
} 

回到主题,我们用属性将数据从DOM中复制到指令的隔离作用域中:

<div my-directive 
    my-url="http://google.com" 
    my-link-text="Click me to go to Google"></div> 
angular.module('myApp', []) 
.directive('myDirective', function() { 
    return { 
        restrict: 'A', 
        replace: true, 
        scope: { 
            myUrl: '@', //绑定策略
            myLinkText: '@' //绑定策略
        }, 
        template: '<a href="{{myUrl}}">' + 
            '{{myLinkText}}</a>' 
    }; 
}); 

默认情况下约定DOM属性和JavaScript中对象属性的名字是一样的(除非对象的属性名采用的是驼峰式写法)。

由于作用域中属性经常是私有的,因此可以(虽然不常见)指定我们希望将这个内部属性同哪个DOM属性进行绑定:

scope: { 
    myUrl: '@someAttr', 
    myLinkText: '@' 
} 

上面的隔离作用域中的内容是:将指令的私有属性$scope.myUrl同DOM中some-attr属性的值绑定起来。这个值既可以是硬编码的也可以是当前作用域(例如
Some-attr="{{expr- ession}})中某个表达式的运算结果。

在DOM中要用some-attr代替my-url:

<div my-directive 
     some-attr="http://google.com" 
     my-link-text="Click me to go to Google" > 
</div> 

在此之上,我们来看看如何创建一个文本输入域,并将输入值同指令内部隔离作用域的属性绑定起来:

注意在输入标签上使用了内置指令ng-model。这个指令可以将输入文本同$scope上的myUrl属性进行绑定。

<input type="text" ng-model="myUrl" /> 
<div my-directive 
    some-attr="{{ myUrl }}" 
    my-link-text="Click me to go to Google"> 
</div> 

更进一步,还可以在DOM对应的作用域上运算表达式,并将结果传递给指令,在指令内部最终被绑定在属性上:

<div my-directive 
     some-attr="{{ 'http://' + 'google.com' }}"> 
</div> 

在此之上,我们来看看如何创建一个文本输入域,并将输入值同指令内部隔离作用域的属性绑定起来:

  • 注意在输入标签上使用了内置指令ng-model。这个指令可以将输入文本同$scope上的myUrl属性进行绑定。
<input type="text" ng-model="myUrl" /> 
<div my-directive 
    some-attr="{{ myUrl }}" 
    my-link-text="Click me to go to Google"> 
</div> 

给两个方向的绑定都添加一个文本输入字段。通过这两个输入字段可以方便地观察作用域是如何在DOM中通过原型继承链接在一起的:

在Chrome开发者工具中一边在两个文本输入字段中输入内容,一边审查href属性的值

<label>Their URL field:</label> 
<input type="text"  ng-model="theirUrl"> 
<div my-directive 
     some-attr="theirUrl" 
     my-link-text="Click me to go to Google"></div> 
angular.module('myApp', []) 
.directive('myDirective', function() { 
    return { 
        restrict: 'A', 
        replace: true, 
        scope: { 
            myUrl: '=someAttr', // 经过了修改
            myLinkText: '@' 
        }, 
        template: '\ 
        <div>\ 
            <label>My Url Field:</label>\ 
            <input type="text"\ 
                ng-model="myUrl" />\ 
            <a href="{{myUrl}}">{{myLinkText}}</a>\ 
        </div>\ 
    }; 
}); 

除了将原来的文本输入字段添加回主HTML文档外,唯一的修改是用=绑定策略代替了@;
总体来说,这个例子展示了双向数据绑定的神奇效果,它是AngularJS的主要卖点之一。
了解内部指令的工作原理非常重要,这样才能在同自定义指令一起使用时把它们的行为考虑在内

posted @ 2015-05-11 10:13  小数  阅读(348)  评论(0编辑  收藏  举报