Angular学习笔记(3)—指令简介

指令:自定义HTML元素和属性

指令本质上就是AngularJS扩展具有自定义功能的HTML元素的途径。

1.HTML引导

在HTML中要用内置指令ng-app标记出应用的根节点。这个指令需要以属性的形式来使用,因此可以将它写到任何位置,写到<html>的开始标签上是最常规的做法。
内置指令是打包在AngularJS内部的指令。所有内置指令的命名空间都使用ng作为前缀。为了防止命名空间冲突,不要在自定义指令前加ng前缀。

2.第一个指令

首先我们创建一个自定义指令:<my-directive></my-directive>,调用指令意味着执行指令背后与之相关联的JS代码,这些代码是我们用指令定义写出来的。
在HTML里使用my-directive声明指令,因此指令定义必须以myDirective为名字。

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

效果如图:


通过.directive()方法,我们可以通过传入一个字符串和一个函数来注册一个新指令。其中字符串是这个指令的名字,指令名应该是驼峰命名风格的,函数应该返回一个对象。directive()方法返回的对象中包含了用来定义和配置指令所需的方法和属性。
默认情况下,AngularJS将模板生成的HTML代码嵌套在自定义标签<my-directive>内部。我们可以将自定义标签从生成的DOM中完全移除掉,并只留下由模板生成的链接。将replace设置为true就可以实现这个效果,replace方法会用自定义元素取代指令声明,而不是嵌套在其内部。

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

效果如图:


我们把创建的这些自定义元素称作指令(用.directive()方法创建),因为事实上声明指令并不需要创建一个新的自定义元素。声明指令本质上是在HTML中通过元素、属性、类或注释来添加功能。
下面都是用来声明指令的合法格式:

<my-directive></my-directive>
<div my-directive></div>
<div class="my-directive"></div>
<!--directive:my-directive-->

为了让AngularJS能够调用我们的指令,需要修改指令定义中的restrict设置。这个设置告诉AngularJS在编译HTML时用哪种声明格式来匹配指令定义。我们可以指定一个或多个格式。可以指定以元素(E)、属性(A)、类(C)或注释(M)的格式来调用指令。

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

无论有多少种方式可以声明指令,我们坚持使用属性方式,因为它有比较好的跨浏览器兼容性。

3.关于IE浏览器

<body>
  <my-directive></my-directive>
  <div my-directive></div>
  <script>
    angular.module('myApp', [])
    .directive('myDirective', function() {
      return {
        restrict: 'AE', // modified line
        template: '<a href="http://google.com">Click me</a>'
      }
    })
  </script>
</body>

如果用IE浏览器打开这段代码,会发现尽管指令声明了两次,但只出现了一个链接。从技术上讲,可以通过在文档头部声明新的标签来解决这个问题,但这样做的后果就是,当疏忽了一致性时会导致额外的问题。因此,最好就是始终用属性来声明指令。另外,扩展内置HTML标签,例如用AngularJS重载<a>、<form><input>。这些场景不会导致浏览器的兼容性问题,因为它们本身就是浏览器所支持的标签。

4.表达式

用表达式或属性调用指令的这两种情况都会在当前作用域中计算一个普通的JS表达式。

<h1 ng-init="greeting='HelloWorld'">
    The greeting is: {{ greeting }}
</h1>
用表达式来声明指令

合法的表达式声明:

<my-directive="someExpression"></my-directive>
<div my-directive="someExpression"></div>
<div class="my-directive:someExpression"></div>
<!-- directive: my-directive someExpression -->

我们在构造这些自定义指令时会创建新的子作用域。

向指令中传递数据

定义指令:

angular.module('myApp',[]).directive('myDirective',function() {
    return {
        restrict: 'A',
        replace: true,
        template: '<a href="{{ myUrl }}">{{ myLinkText }}</a>'
    }
});

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

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

重新加载页面,声明指令的部分已经被模板代替,但是链接的href属性是空的,并且尖括号内也没有文本。
有好几种途径可以设置指令内部作用域中属性的值。AngularJS允许通过创建新的子作用域或者隔离作用域来设置指令内部作用域中属性的值。

<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"></div>
angular.module('myApp',[]).directive('myDirective',function() {
    return {
        restrict: 'A',
        replace: true,
        scope: {
            myUrl: '@', //绑定策略
            myLinkText: '@' //绑定策略
        },
        template: '<a href="{{myUrl}}">{{myLinkText}}</a>'
    };
});

默认情况下约定DOM属性和JS中对象属性的名字是一样的。
由于作用域中属性经常是私有的,因此可以指定我们希望将这个内部属性同哪个DOM属性进行绑定。

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

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

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

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

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

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

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

这段代码是可以工作的,但如果我们将文本输入字段移到指令内部并在另一个指令中进行绑定,就无法正常工作了。

<div my-directive some-attr="{{myUrl}}" my-link-text="Click me"></div>

template: 
    '<div>
        <input type="text" ng-model="myUrl" /><a href="{{myUrl}}">{{myLinkText}}</a>
    </div>'

出现这种现象的原因是,内置指令ng-model在它自身内部的隔离作用域和DOM的作用域(由控制器提供)之间创建了一个双向数据绑定。
让我们来模仿一下这个设置过程以使例子能正工作。接下来在我们的隔离作用域和ng-model内部的隔离作用域之间创建一个双向数据绑定。将内部的$scope.myUrl属性同当前控制器作用域中theirUrl属性进行绑定,在DOM中通过作用域查询来实现这个绑定。
在这个流程中,给两个方向的绑定都添加一个文本输入字段。通过这两个输入字段可以方便地观察作用域是如何在DOM中通过原型继承链接在一起的。

<label>Their URL field:</label>
<input type="text" ng-model="theirUrl">
<div my-directive some-attr="theirUrl" my-link-text="Click me"></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>
    };
});
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容