《AngularJS权威教程》笔记(8-10)——指令简介、内置指令、指令详解

1. 指令简介

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

[1] HTML引导

在HTML中要用内置指令ng-app标记出应用的根节点。这个指令需要以属性形式使用,可以写到任何位置,但是写到<html>的开始标签上是最常规的做法。

<html ng-app='myApp'>
    <!-- 应用的$rootScope -->
</html>

[2] 我们的第一个指令

下例创建了一个自定义指令。通过AngularJS模块API中的.directive()方法,通过传入一个字符串(指令名称)和一个函数(返回一个对象)来注册一个新指令。

注意:指令名称应用驼峰命名。

<my-directive></my-directive>
angular.module('myApp',[])
  .directive('myDirective',function(){
    return {
      restrict: 'E',
      template: '<a href="https://baidu.com">Click me to go to Baidu</a>'
    }
  })

这里写图片描述
这样的方式生成的代码如下:

<my-directive>
    <a href="https://baidu.com">Click me to go to Baidu</a>
</my-directive>

但是,我们并不想多增加一个节点,看到my-directive这样的标签,可以在创建是增加replace属性。replace方法会用自定义元素取代指令声明,而不是嵌套在其内部。

angular.module('myApp',[])
.directive('myDirective',function(){
  return {
    restrict: 'E',
    replace: true, //----看这里
    template: '<a href="https://baidu.com">Click me to go to Baidu</a>'
  }
})

得到代码:

<a href="https://baidu.com">Click me to go to Baidu</a>

[3] 创建合法指令

下面都是创建指令的合法格式:

 <my-directive></my-directive>       <!--元素(E)-->
 <div my-directive></div>            <!--属性(A)-->
 <div class='my-directive'></div>    <!--类  (C)-->
 <!-- directive:my-directive -->     <!--注释(M)-->

主要在指令定义中的restrict设置。说明可以用哪种声明格式来匹配指令定义。

上例中,restrict: 'E',表明只能使用元素的方式。如果允许所有类型,可改为restrict: 'EACM',以此类推。

推荐使用属性方式,也就是A,因为有比较好的跨浏览器兼容性。特别对于IE浏览器。

[4] 表达式

指令可以用属性的形式调用,尝试下给属性赋值:

<h1 ng-init="greeting='Hello World'">
  The greeting is {{greeting}}
</h1>

得到结果为:The greeting is Hello World

上例中,将表达式greeting='Hello World'赋值给内置指令gn-init

(2) 向指令中传递数据

[1] 传递数据

上面go to baidu的那个例子中,如果不将URL和链接文本混在指令内部,可以提供更好的体验。所以,让其接受两个变量。
改为:

<div my-directive my-url='http://baidu.com' my-link-text='Click me to go to Baidu'></div> 
template: '<a href="{{myUrl}}">{{myLinkText}}</a>'

运行结果并未生效。AngularJS允许通过创建新的子作用域或者隔离作用域来解决这个问题。

angular.module('myApp',[])
.directive('myDirective',function(){
  return {
    restrict: 'A',
    replace: true,
    scope:{
      myUrl: '@', //----看这里,绑定策略
      myLinkText: '@'//----看这里,绑定策略
    },
    template: '<a href="{{myUrl}}">{{myLinkText}}</a>'
  }
})

[2] 动态传递

创建一个文本输入域,将输入的值同指令内部隔离作用域的属性绑定起来。
这里写图片描述

这是书上的实现方式,使用了some-attr进行中转:

<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 Baidu'></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>'
  }
})

尝试不适用中转也是可以的:

<label>Their URL field:</label>
<input type="text" ng-model="theirUrl">
<div my-directive 
    my-url='{{theirUrl}}' 
    my-link-text='Click me to go to Baidu'></div> 
angular.module('myApp',[])
.directive('myDirective',function(){
  return {
    restrict: 'A',
    replace: true,
    scope:{
      myUrl: '@',
      myLinkText: '@'
    },
    template: '<div><label>My URL field:</label><input type="text" ng-model="myUrl"><a href="{{myUrl}}">{{myLinkText}}</a></div>'
  }
})

2. 内置指令

所有以ng前缀开头作为命名控件指令都是AngularJS提供的内置指令。因此,不要用ng作为自定义指令的命名前缀。

(1) 基础ng属性指令

以下是和原生HTML标签名称相似的内置指令,仅仅加了ng前缀。

  • ng-href
  • ng-src
  • ng-disabled
  • ng-checked
  • ng-readonly
  • ng-selected
  • ng-class
  • ng-style

[1] 布尔属性

根据HTML标准的定义,布尔属性代表一个true或者false值。当这个属性出现时,值就为true(无论实际定义的值是什么)。如果未出现,值为false。

1) ng-disabled

可以绑定到以下表单输入字段上:

  • <input>
  • <textarea>
  • <select>
  • <button>

例子:按钮会一直禁用,知道用户在input中输入内容:
这里写图片描述

<input type="text" ng-model="someProperty" placeholder="TypetoEnable">
<button ng-model='button' ng-disabled='!someProperty'>AButton</button>

2) ng-readonly

第一个文本框输入的时候,后一个文本框中的内容为只读。
这里写图片描述

<input type="text" ng-model="someProperty">
<input type="text" ng-readonly="someProperty" value="Some text here">

3) ng-checked

下例,通过ng-init指令将someProperty的值设置为true,将someProperty和ng-checked绑定在一起,这样默认把复选框勾选。点击取消勾选,someProperty的值也会跟着变化。
这里写图片描述

<label>someProperty={{someProperty}}</label>
<input type="checkbox" ng-checked='someProperty' ng-init='someProperty=true' ng-model="someProperty">

4) ng-selected

下例,点选了checkbox后,select会自动切换到对应的option。
这里写图片描述

<label>Select Two Fish:</label>
<input type="checkbox" ng-model='isTwoFish'>
<select>
  <option>One Fish</option>
  <option ng-selected="isTwoFish">Two Fish</option>
</select>

[1] 类布尔属性

ng-href和ng-src虽不是标准的HTML布尔属性,但行为相似,在AngularJS内部是同等对待的。这两个属性能有效帮助重构和避免错误,强烈建议代替原来的href和src属性。

1) ng-href

当前作用域中的属性动态创建URL,应用ng-href代替href。

下例说明了使用href的潜在问题,用户点击时,若插值未生效,将会跳转到错误页面(IE会出现此种情况)。

<a ng-href="{{myHref}}">I'm felling lucky, when I load</a>
<a href="{{myHref}}">I'm felling 404</a>
angular.module('myApp',[])
.run(function($rootScope,$timeout){
  $timeout(function(){
    $rootScope.myHref='http://baidu.com'
  },2000);
});

2) ng-src

道理同上。

(2) 在指令中使用作用域

以下指令会以父级作用域为原型生成子作用域。这种继承的机制可以创建一个隔离层,用来将需要协同工作的方法和数据模型对象放置在一起。

ng-app和ng-controller是特殊的指令,因为会修改嵌套在他们内部的指令的作用域。

[1] ng-app

任何具有ng-app属性的DOM元素将被标记为$rootScope的起始点。
这里写图片描述

<!doctype html>
<html ng-app='myApp'>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
    <script src="/js/angular.min.js"></script>
  </head>
  <body>
    {{someProperty}}
    <button ng-click="someAction()">human button</button>
  </body>
  <script>
    angular.module('myApp',[])
    .run(function($rootScope){
      //只是演示,不建议这么用!!!
      $rootScope.someProperty='Hello computer';
      $rootScope.someAction=function(){
        $rootScope.someProperty='Hello human';
      };
    });
  </script>
</html>

[2] ng-controller

作用是为嵌套在其中的指令创建一个子作用域,避免所有操作和模型都定义在$rootScope上。

将上例改造一下,将数据和操作放到子作用域中:

<div ng-controller='SomeController'>
  {{someModel.someProperty}}
  <button ng-click="someAction()">human button</button>
</div>
angular.module('myApp',[])
.controller('SomeController',function($scope){
  $scope.someModel={
    someProperty:'Hello computer'
  }
  $scope.someAction=function(){
    $scope.someModel.someProperty='Hello human';
  };
});

使用子作用域意味着其上的数据模型和操作在应用的其他地方是无法访问的,只能被这个作用域内的指令和子作用域访问。显式声明数据模型非常重要。

下例中,进行了嵌套。由于原型的继承关系,修改父级对象中的someBareValue会同时修改子对象中的值,反之不行。这个例子充分说明子控制器是复制而非引用someBareValue。

这里写图片描述

<div ng-controller='SomeController'>
  {{someBareValue}}
  <button ng-click="someAction()">communicate to child</button>
  <div ng-controller='ChildController'>
    {{someBareValue}}
    <button ng-click="childAction()">communicate to parent</button>
  </div>
</div>
angular.module('myApp',[])
.controller('SomeController',function($scope){
  $scope.someBareValue='Hello computer';//错误示范
  $scope.someAction=function(){
    $scope.someBareValue='Hello human,from parent';
  };
})
.controller('ChildController',function($scope){
  $scope.childAction=function(){
    $scope.someBareValue='Hello human,from child';
  };
});

如果将模型对象的某个属性设置成对象,就会通过引用进行共享,在子$scope中修改属性也会修改父$scope中的这个属性。

<div ng-controller='SomeController'>
  {{someModel.someValue}}
  <button ng-click="someAction()">communicate to child</button>
  <div ng-controller='ChildController'>
    {{someModel.someValue}}
    <button ng-click="childAction()">communicate to parent</button>
  </div>
</div>
angular.module('myApp',[])
.controller('SomeController',function($scope){
  $scope.someModel={
    someValue:'Hello computer' //正确示范
  };
  $scope.someAction=function(){
    $scope.someModel.someValue='Hello human,from parent';
  };
})
.controller('ChildController',function($scope){
  $scope.childAction=function(){
    $scope.someModel.someValue='Hello human,from child';
  };
});

此时,不管先点击哪个按钮,值都会同步修改。

[3] ng-include

可以加载、编译并包含外部HTML片段到当前的应用中。

在同一个元素上添加onload属性可以在模板加载完成后执行一个表达式。

使用ng-include时,会自动创建一个子作用域。如果想使用某个特定作用域,需在同一个DOM元素上添加ng-controller指令。这样,当模板加载完成后,不会从外部作用域继承并创建一个新的子作用域。

<!--myTemplateName.html-->
<p>Hello {{name}}, I am been included</p>
<div ng-include="'myTemplateName.html'"
      ng-controller="MyController" ng-init="name='World'" onload="showGreeting()">
    </div>
angular.module('myApp',[])
.controller('MyController',function($scope){
  $scope.showGreeting=function(){
    console.log($scope.name);
  }
});

注意:ng-include中为一个字符串,双引号中还有单引号。
运行代码: Hello World, I am included,并在console里面打印出World。

[4] ng-switch

这个指令和ng-switch-when及on=”propertyName”一起使用,可以在propertyName发生变化时,渲染不同指令到视图中。

下例,当输入Ari时,文本显示Yes, it’s Ari,其余时候显示Guess the winner。

<input type="text" ng-model='person.name'>
<div ng-switch on="person.name">
  <p ng-switch-default>Guess the winner</p>
  <h1 ng-switch-when='Ari'>Yes, it's {{person.name}}</h1>
</div>

[5] ng-view

用来设置将被路由管理和放置在HTML中的视图的位置。详见12章

[6] ng-ifng-show/ng-hide

根据表达式的值(true或者false)在DOM中生成或移除一个元素。

ng-ifng-showng-hide的本质区别是,并非通过css显示或者隐藏某个节点,而是生成和移除。
这里写图片描述

<input type="checkbox" ng-model='insertElement'>
<span>Select to insert new element</span>
<h3 ng-if='insertElement'>I am inserted</h3>

[7] ng-repeat

用来遍历一个集合或为集合中的每个元素生成一个模板实例。集合中的每个元素都会被赋予自己的模板和作用域。同时,每个模板实例的作用域中都会暴露一些特殊的属性。

  • $index: 遍历的进度(0…length-1)
  • $first: 当元素是遍历的是第一个时,值为true
  • $middle: 当元素处于非第一个和最后一个元素时,值为true
  • $last 当元素是遍历的最后一个时,值为true
  • $even$index是偶数时,值为true
  • $odd$index是奇数时,值为true

    下例制作颜色相间的表格,由于索引是从0开始,所以使用!$even!$odd进行布尔值反转。
    这里写图片描述

.odd{
 background-color: wheat;
}
.even{
  background-color: grey;
}
<ul ng-controller='PeopleController'>
  <li ng-repeat='person in people' ng-class="{even: !$even, odd: !$odd}">
    {{person.name}} lives in {{person.city}}
  </li>
</ul>
angular.module('myApp',[])
.controller('PeopleController',function($scope){
  $scope.people=[
    {name:'Ari',city:'San Francisco'},
    {name:'Amy',city:'Beijing'},
    {name:'Joyce',city:'New York'},
    {name:'Darren',city:'Chengdu'},
    {name:'Erik',city:'Seattle'}
  ]
});

[8] ng-init

用来在指令被调用时设置内部作用域的初始状态。

常见应用场景为需要创建小的示例代码时。对于需要健壮结构的场景,需在控制器中用数据模型对象来设置。

<div ng-init="greeting='Hello';person='World'">
  {{greeting}} {{person}}
</div>

[9] {{}}ng-bind

AngularJS内置的模板语法,它会在内部$scope和视图之间创建绑定。只要$scope发生变化,视图就会自动更新。

{{}}ng-bind的简略形式。在屏幕可视区域使用会导致页面加载时未渲染元素发生闪烁,用ng-bind可避免这个问题。

[10] ng-cloak

除了使用ng-bind来避免未渲染元素闪烁,还可以在含有{{}}的元素上使用ng-cloak指令。

<div ng-init="greeting='Hello'">
  <p ng-cloak>{{greeting}}</p>
</div>

[11] ng-bind-template

ng-bind指令类似,ng-bind-template用来在视图中绑定多个表达式。

<div ng-init="greeting='Hello';person='world'">
  <p ng-bind-template="{{greeting}} {{person}}"></p>
</div>

[12] ng-model

用来将input、select、text area或自定义表单控件同包含他们的作用域中的属性进行绑定。如果属性不存在,会隐式创建并添加到当前作用域。

<input type="text" ng-model='modelName.someProperty'>
<p>{{modelName.someProperty}}</p>

[13] ng-change

会在表单输入发生变化时,计算给定表达式的值。需要和ng-model联合使用。
这里写图片描述

<div ng-controller="EquationController">
  <input type="text" ng-model='equation.x' ng-change='change()'>
  +
  <input type="text" ng-model='equation.y' ng-change='change()'>
  =
  <code>{{equation.output}}</code>
</div>
angular.module('myApp',[])
.controller('EquationController',function($scope){
  $scope.equation={};
  $scope.change=function(){
    var x=$scope.equation.x?parseInt($scope.equation.x):0;
    var y=$scope.equation.y?parseInt($scope.equation.y):0;
    $scope.equation.output=x+y;
  };
});

[14] ng-form

用来在一个表单内部嵌套另一个表单。
以下CSS类会根据表单的验证状态自动设置:

  • 表单合法时设置ng-valid
  • 表单不合法时设置ng-invalid
  • 表单未进行修改时设置ng-pristion
  • 表单进行过修改时设置ng-dirty

这里写图片描述

input.ng-invalid{ border: 1px solid red;}
input.ng-valid{ border: 1px solid green;}
<form name='signup_form' ng-controller="FormController" ng-submit='submitForm()' novalidate>
  <div ng-repeat='field in fields' ng-form='signup_form_input'>
    <input type="text" name='dynamic_input' ng-required="field.isRequired" ng-model='field.name' placeholder="{{field.placeholder}}">
    <span class='error' ng-show='signup_form_input.dynamic_input.$error.required'>The field is required.</span>
  </div>
  <button type='submit' ng-disabled="signup_form.$invalid">submit</button>
</form>
angular.module('myApp',[])
.controller('FormController',function($scope){
  $scope.fields=[
    {placeholder:'Uername',isRequired:true},
    {placeholder:'Password',isRequired:true},
    {placeholder:'Email(optional)',isRequired:false}
  ];
  $scope.submitForm=function(){
    alert('it works');
  };
});

[15] ng-click

用来指定一个元素被点击时调用的方法或表达式。
这里写图片描述

<div ng-controller='CounterController'>
  <button ng-click='count = count + 1' ng-init="count=0">Increase</button>
  count: {{count}}
  <button ng-click="decrease()">Decrease</button>
</div>
angular.module('myApp',[])
.controller('CounterController',function($scope){
  $scope.decrease=function(){
    $scope.count=$scope.count -1;
  };
});

[16] ng-select

用来将数据同<select>元素绑定。这个指令可以和ng-modelng-options一同使用。
ng-options的值是一个内涵表达式,可以接受一个数组或者对象,并对他们进行循环,将内部的内容提供给select标签内部的选项。
这里写图片描述

<div ng-controller='CityController'>
  <select ng-model='city' ng-options='city.name for city in cities'>
    <option value="">Choose City</option>
  </select>
  Best City: {{city.name}}
</div>
angular.module('myApp',[])
.controller('CityController',function($scope){
  $scope.cities=[
    {name:'Seattle'},
    {name:'San Francisco'},
    {name:'Chicago'},
    {name:'New York'},
  ]
});

[17] ng-submit

用来将表达式同onsubmit事件进行绑定。这个指令同时会阻止默认行为(发送请求并重新加载页面)。

下面这个例子,在input输入内容回车会显示出来。
这里写图片描述

<form ng-controller="FormController" ng-submit='submit()' >
  Enter text and hit enter:
  <input type="text" ng-model='person.name' name='person.name' />
  <br>
  <code>people={{people}}</code>
  <ul ng-repeat="(index,object) in people">
    <li>{{object.name}}</li>
  </ul>
</form>
angular.module('myApp',[])
.controller('FormController',function($scope){
  $scope.person={
    name: null
  };
  $scope.people=[];
  $scope.submit=function(){
    if($scope.person.name){
      $scope.people.push({name: $scope.person.name});
      $scope.person.name='';
    }
  };
});

[18] ng-class

动态设置元素的类,方法是绑定一个代表所有需要添加的类的表达式。当表达式发生变化,先前添加的类会被移除,新类会被添加。

下例,生成1-10的随机数,大于5时,显示红色的You won!
这里写图片描述

<div ng-controller="LotteryController">
  <button ng-click="x=generateNumber()" ng-init="x=0">Draw Number</button>
  <div ng-class="{red: x>5}" ng-if="x> 5">You won!</div>
  <p>Number is: {{x}}</p>
</div>
angular.module('myApp',[])
.controller('LotteryController',function($scope){
  $scope.generateNumber=function(){
    return Math.floor((Math.random()*10)+1);
  };
});

[18] ng-attr-(suffix)

当AngularJS编译DOM时会查找花括号内的表达式,这些表达式会被自动注册到$watch服务中并更新到$digest循环中,成为它的一部分。

有时浏览器会对属性进行很苛刻的限制,如下例子:
这里写图片描述

<!--错误示例-->
 <svg ng-init="cx=100">
    <circle cx="{{cx}}" cy="50" r="40" fill="wheat"/>
</svg>

会报错,指出有一个非法属性。
这里写图片描述
可用ng-attr-cx来解决:

<svg ng-init="cx=100">
    <circle ng-attr-cx="{{cx}}" cy="50" r="40" fill="wheat"/>
</svg>

3. 指令详解

(1) 指令定义

angular.module('myApp',[])
 .directive('myDirective',function(){
     //一个指令定义对象
     return{
         //通过设置项来定义指令,在这里进行覆写
     }
 }

定义一个指令可以使用的全部设置选项:
这里写图片描述

[1] restrict(字符串)

E(元素)、A(属性,默认值)、C(类名)、M(注释)

[2] priority(数值)

优先级参数可以设置为一个数值。默认值为0。

大多指令忽略该参数,但有些场景必须。例如,ng-repeat设置为1000,可以保证在同一元素上,总是在其他指令之前被调用。

[3] terminal(布尔型)

设置是否停止运行当前元素上比本指令优先级低的指令。

例如ng-view和ng-if。如果ng-if为true,ng-view正常执行;为false,ng-view(优先级较低)不执行。

[4] template(字符串或函数)

必须存在一个根DOM元素,每行末尾的反斜线,确保正确解析多行字符串。

实际开发中,最好使用templateUrl引用外部模板,因为多行文本维护难度大。

template:'\
    <div><!--single root element-->\
        <a>Click me</a>\
        <p>Hello</p>\
    </div>\
    '

[5] templateUrl(字符串或函数)

默认情况下,会在后台通过Ajax来请求HMTL模板文件。模板加载是异步的,意味着编译和链接要暂停,等待模板加载完成。

[6] replace(布尔型)

可选参数,默认值为false。默认值意味着模板会被当做子元素插入到调用此指令的元素内部。

(2) 指令作用域

[1] scope(布尔型或对象)

可选参数,默认值为false。作用域的继承机制是向下而非向上的。

当scope设置为true时,会从父作用域继承并创建一个新的作用域对象。内置指令ng-controller就是这样的,创建一个新的从父作用域继承而来的子作用域。

<div ng-init="div1Property='div 1 property'">
  Inside Div1:{{div2Property}}

  <div ng-init="div2Property='div 2 property'" ng-controller="div2Controller">
    Inside Div2: {{div2Property}}

    <div ng-controller='dvi3Controller'>
        Inside Div3: {{div2Property}}
        <br>

        Inside Div3, Outside myDirective: {{directiveProperty}}
        <div my-directive ng-init="directiveProperty='directive property'">
            Inside Div3, Inside myDirective: {{directiveProperty}} 
        </div>
    </div>
  </div>
</div>
angular.module('myApp',[])
.controller('div2Controller',function($scope){})
.controller('dvi3Controller',function($scope){})
.directive('myDirective',function(){
  return{
    restrict: 'A',
    scope: true //----看这里
  }
})

从下图的结果中可以看出作用域的继承机制是向下,在div3中依旧可以访问div2中的属性,而在div1中却不行。

scope属性的区别体现在下图红框中。如果scope为true,意味着新建了一个作用域,所以在指令之外访问不到这个属性。而当scope为false的时候,指令在div3这个作用域中,所以在指令外依旧可以访问。
这里写图片描述

[2] 隔离作用域

隔离作用域的指令最主要的使用场景是创建可复用的组件,组件可以在未知上下文中使用,指令的模板无法访问外部作用域。

创建隔离作用域,设置scope为一个空对象{}。下表进行了对比:

scope的值 产生一个子作用域(作用域范围只有template) 继承父作用域
false × -
true
{} ×
<div ng-init="myProperty='wow, this is cool'">
  surrounding scope: {{myProperty}}
  <div my-directive-obj ></div>
  <div my-directive-true ></div>
</div>
angular.module('myApp',[])
.directive('myDirectiveObj',function(){
  return{
    restrict: 'A',
    scope: {}, //----看这里
    template:'<div>Inside myDirective({}): {{myProperty}}</div>'
  }
})
.directive('myDirectiveTrue',function(){
  return{
    restrict: 'A',
    scope: true, //----看这里
    template:'<div>Inside myDirective(true): {{myProperty}}</div>'
  }
})

运行结果为:
surrounding scope: wow, this is cool
Inside myDirective({}):
Inside myDirective(true): wow, this is cool

(3) 绑定策略

AngularJS提供了几种方法能够将指令内部的隔离作用域与外部作用域进行数据绑定,需要使用以下三种中的一种:

  • @(或者 @attr),把当前的属性作为字符串传递
  • =(或者 =attr),与父scope中的属性进行双向绑定
  • &(或者 &attr),传递来自父scope中的函数

[1] @

把当前的属性作为字符串传递

<div ng-controller="MyController">  
  <div my-directive flavor="{{ctrlFlavor}}"></div>  
</div>  
angular.module('myApp',[])
.controller('MyController',function($scope){  
  $scope.ctrlFlavor="鸡尾酒";  
})
.directive("myDirective",function(){  
  return{  
    restrict:'A',  
    scope:{  
      flavor:'@'  //自动绑定,传递的是字符串  
    },  
    template:"<div>{{flavor}}</div>",  
  };  
});  

运行结果为:鸡尾酒

指令中有一个flavor属性,其外层的div中的scope有一个ctrlFlavor属性,上面的例子实现外层div中scope的值绑定在指令的flavor属性上。

[2] =

与父scope中的属性进行双向绑定

<div ng-controller="MyController">  
  <p>{{ctrlFlavor}}</p>
  <div my-directive flavor="ctrlFlavor"></div>  
</div> 
angular.module('myApp',[])
.controller('MyController',function($scope){  
  $scope.ctrlFlavor="鸡尾酒";  
})
.directive("myDirective",function(){  
  return{  
    restrict:'A',  
    scope:{  
      flavor:'='  //双向绑定
    },  
    template:'<input type="text" ng-model="flavor"/>',  
  };  
});  

这里写图片描述

指令中的flavor属性是和外部作用域中的ctrlFlavor双向绑定的。指令中的值发生变化,作用域中的ctrlFlavor会跟着变化。

[3] &

传递来自父scope中的函数

<div ng-controller="MyController">  
  <div my-directive greet="sayHello(name)"></div>  
  <div my-directive greet="sayHello(name)"></div>  
  <div my-directive greet="sayHello(name)"></div>  
</div>  
angular.module('myApp',[])
.controller('MyController',function($scope){  
  $scope.sayHello=function(name){
    alert('Hello,'+name);
  };  
})
.directive("myDirective",function(){  
  return{  
    restrict:'A',  
    scope:{  
      greet:'&'
    },  
    template:'<input type="text" ng-model="userName" /><br/>'+  
      '<button ng-click="greet({name:userName})">问候一下</button><br/>'  
  };  
});  

这里写图片描述
在div中绑定一个Controller,这个Controller中定义了一个sayHello的方法,在div中又定义了一个名为myDirective的指令,这个指令的greet方法与父层scope的sayHello相互关联,父scope中的函数就传递到了子scope中。

[4] transclude(布尔型)

默认值为false。

关于这个属性,看文档根本没有看懂,并不理解为啥设置这个属性,以及与replace的区别。下面详细讲解如下:

首先,看下replace:

<hello>  
  <div>Hello everyone!</div>  
</hello> 
 angular.module('myApp',[])
 .directive("hello", function() {  
     return {  
         restrict:"AE",  
         replace:true  //---看这里
         template:"<div>My name is Joyce</div>",  
     }   
 });  

运行结果为:My name is Joyce

我们发现,template里面的内容会彻底替代hello标签内的内容。这并不是希望的结果,因为这种方式完全无法完成标签的嵌套。于是,有了transclude。

在模板中添加了<div ng-transclude></div>就是告诉ng把hello标签内部的内容全部放在ng-transclude所在的位置。

angular.module('myApp',[])
.directive("hello", function() {  
  return {  
    restrict:"AE",  
    transclude: true, //---看这里
    template:"<div ng-transclude></div><div>My name is Joyce</div>",  
  }   
});  

运行结果为:
Hello everyone!
My name is Joyce

如下图,hello内部原来的DOM结构被这个ng-transclude指定的div包裹起来了,而且内部原来的DOM也被添加了一个内置的class,也就是ng-scope,这一点一定要注意!
这里写图片描述

[5] controller(字符串或函数)

当设置为字符串时,查找注册在应用中的控制器的构造函数。

angular.module('myApp', [])
.directive('myDirective',  function() {
    restrict: 'A', 
    controller: 'SomeController'
})

当设置为函数时,在指令内部通过匿名构造函数来定义内联的控制器。

angular.module('myApp',[])
.directive('myDirective', function() {
    restrict: 'A',
    controller:
    function($scope, $element, $attrs, $transclude) {
        // 控制器逻辑放在这里 
    }
});

1)$scope

与指令元素相关联的当前作用域

2)$element

当前指令对应的元素

3)$attrs

有当前元素的属性组成的对象,例如:

<div id='myDiv' class='container' my-directive ></div>

属性对象为:

{
    id:'myDiv',
    class:'container'
}

4)$transclude

嵌入链接函数会与对应的嵌入作用域进行预绑定。transclude链接函数是实际被执行用来客隆元素和操作DOM的函数

在控制器内部操作DOM是和Angular风格相悖的做饭,但通过链接函数就可以实现这个需求。仅在compile参数中使用transcludeFn是推荐的做法

此处,关于这个部分就不详细介绍了。

[5] controllerAs(字符串或函数)

controllerAs参数用来设置控制器的别名,可以在视图中引用控制器,无需注入$scope

如下例:

<div ng-controller='MainController as main'>
  <span>{{main.name}}</span>
</div>
angular.module('myApp',[])
.controller('MainController', function() {
  this.name='Amy';
});

这个参数可以在路由和指令中创建匿名控制器。可以将动态的对象创建成为控制器,并且这个对象是隔离的,易于测试的。如下:

<div my-directive></div>
angular.module('myApp',[])
.directive("myDirective", function() {  
  return {  
    restrict:"A",  
    template:'<h4>{{myController.msg}}</h4>',
    controllerAs: 'myController',
    controller: function(){
      this.msg='Hello World'
    }
  }   
});  

[5] require(字符串或数组)

require的作用是为了让父子指令或者兄弟指令的controller之间搭建一个桥梁。也就是说父指令里的controller里面的数据能分享给子指令的controller。

其中子指令的link第四个参数的值是父指令的controller对象的作用域上下文require有两个修饰符号:?^

  • ? : 如果require没有找到相应的指令避免报错,还能确保程序的正常执行
  • ^ : 表示往父级查找

(4) AngularJS生命周期

这部分书上有比较详细的说明,在此,仅介绍两个比较重要的部分compile和link。

compile可以安全的修改DOM,link则不能。换句话说,compile在修改了DOM后进行编译,link修改了DOM后不再自动编译。

[1] compile(对象或函数)

通常情况下,如果设置了compile函数,说明希望在指令和实时数据被放到DOM之前进行DOM操作,在这个函数中进行诸如添加和删除节点等DOM操作是安全的。

angular.module('myApp',[])
.directive("myDirective", function() {  
  return {
      restrict: 'A',
      compile: function(tElem, tAttrs,transcludeFn){
      }
  }
});  

用link函数创建可以操作DOM的指令。
这里写图片描述

如果指令中有require选项,函数签名中会有第四个参数,代表控制器或者所以来的指令的控制器。

angular.module('myApp',[])
.directive("myDirective", function() {  
  return {
      restrict: 'A',
      require: 'SomeController',
      link: function(scope,element,attrs,SomeController){
      }
  }
});  

参数:
- scope: 指令用来在其内部注册监听器的作用域
- iElement: 代表实例元素,指使用此指令的元素。
- iAttrs: 代表实例属性,会以JavaScript对象的形式进行传递。
- controller: 指向require选项定义的控制器。如果没有设置require选项,这个参数值为undefined

猜你喜欢

转载自blog.csdn.net/joyce_lcy/article/details/79670971