Angular指令

Angular指令概述

指令是一种执行的信号,一旦发布了这个指令,就要执行某项动作,HTML中的标签都可以理解为指令,但Angular中的指令要复杂很多,不但要创建元素,还要给元素附加某些特定的行为,因此,Angular中的指令是一个在特定DOM元素上执行的函数

指令定义的基础

在Angular中,定义指令需要调用directive方法,该方法接收两个参数:

var app = angular.module('myapp', []);
app.directive(name, fn);

在定义的模块上使用directive方法创建一个指令,name为指令的名称,fn是一个函数,它将返回一个对象,在这个对象中,定义了这个指令的全部行为

<div>
  <ts-hello></ts-hello>
  <div ts-hello></div>
  <div class="ts-hello"></div>
  <div data-ts-hello></div>
</div>
<script>
  var app = angular.module('myapp', []);
  app.directive('tsHello', function () {
    return {
      restrict: 'EAC',
      template: '<h3>Hello, Angular!</h3>'
    }
  })
</script>

设置指令对象的基础属性

replace:它的属性值是布尔类型,当该属性值为true时,表示用模板中的内容替换指令标记,否则则不替换,直接显示指令标记,默认为false

templateUrl:它的属性值是一个URL地址,该地址指向一个模板页面

<div>
  <ts-tplfile></ts-tplfile>
  <ts-tplscript></ts-tplscript>
  <ts-tplcache></ts-tplcache>
</div>
<script>
  var app = angular.module('myapp', []);

  app.run(function ($templateCache) {
    $templateCache.put('cache', '<h3>模板内容来源于缓存</h3>')
  });

  app.directive('tsTplfile', function () {
    return {
      restrict: 'EAC',
      templateUrl: 'tpl.html'
    };
  });

  app.directive('tsTplscript', function () {
    return {
      restrict: 'EAC',
      templateUrl: 'tpl',
      replace: true
    }
  });

  app.directive('tsTplcache', function () {
    return {
      restrict: 'EAC',
      templateUrl: 'cache'
    }
  });
</script>

Angular指令对象的重要属性

指令对象中的transclude属性

transclude属性的值是布尔值,默认为false,表示不开启,如果设置为true,则开启该属性,当开启后,则可以在模板中通过ng-transclude方式替换指令元素中的内容

<ts-hello>
  <div>我是自定义指令在HTML里的内容</div>
</ts-hello>
<script>
  var app = angular.module('MyApp', []);
  app.directive('tsHello', function () {
    return {
      restrict: 'EA',
      template: '<div ng-transclude></div>',
      replace: true,
      transclude: true
    }
  });
</script>

指令对象中的link属性

指令对象中的link属性的值是一个函数,在该函数中可以操控DOM元素对象,包括绑定元素的各类事件,定义事件触发时执行的内容

link: function(scope, element, attrs) {
  // ...
}

link函数包含3个主要的参数:

  • scope参数表示指令所在的作用域
  • element参数表示指令中的元素,改元素可以通过Angular内部封装的jqLite框架进行调用
  • attrs参数表示指令元素的属性集合通过这个参数可以获取元素中的各类属性
<script type="text/ng-template" id="tpl">
  <button>点击按钮</button>
</script>

<div>
  <ts-tplscipt></ts-tplscipt>
  <div>{{content}}</div>
</div>
<script>
  var app = angular.module('myapp', []);
  app.directive('tsTplscipt', function () {
    return {
      restrict: 'EAC',
      templateUrl: 'tpl',
      replace: true,
      link: function (scope, element, attrs) {
        element.bind('click', function () {
          scope.$apply(function () {
            scope.content = '这是点击后显示的内容';
          })
          attrs.$$element[0].disabled = true;
        })
      }
    }
  });
</script>

指令对象中的compile属性

该属性比起link属性使用的很少,该属性返回一个函数或对象。当返回一个函数时,该函数名称为post,而返回一个对象时,该对象中则包含两个名为prepost方法函数,这两个函数名是系统提供的,不可修改。

<div ng-controller="myctrl">
  <ts-a>
    <ts-b>
      {{tip}}
    </ts-b>
  </ts-a>
</div>
<script>
  var app = angular.module('myapp', []);
  app.controller('myctrl', function ($scope) {
    $scope.tip = '跟踪compile执行过程';
  });

  app.directive('tsA', function () {
    return {
      restrict: 'EAC',
      compile: function (tEle, tAttrs, trans) {
        console.log('正在编译A指令');
        return {
          pre: function (scope, element, attrs) {
            console.log('正在执行A指令中的pre函数');
          },
          post: function (scope, element, attr) {
            console.log('正在执行A指令中的post函数');
          }
        }
      }
    }
  });

  app.directive('tsB', function () {
    return {
      restrict: 'EAC',
      compile: function (tEle, tAttrs, trans) {
        console.log('正在编译B指令');
        return {
          pre: function (scope, element, attrs) {
            console.log('正在执行B指令中pre函数');
          },
          post: function (scope, element, attrs) {
            console.log('正在执行B指令中的post函数');
          }
        }
      }
    }
  });
</script>

Angular指令对象的scope属性

在Angular指令对象中,scope属性使用频率很高,它的值包含两种类型,一种是布尔值,另一类是JSON对象

scope属性是布尔值

使用scope属性自定义指令时,默认是布尔类型,初始值为false。在这种情况下,指令中的作用域就是指令元素所在的作用域。我们将指令中的作用域称为子作用域,把指令元素所在作用域称为父作用域,当scopefalse时,子作用域和父作用域完全相同,一方变化,则另一方也会自动发生变化

scopetrue时,则表示子作用域是独立创建的,父作用域中内容的改变会影响子作用域,但子作用域的内容发生变化,并不会修改父作用域中的内容

<script type="text/ng-template" id="tpl">
  <div>{{message}}</div>
  <button ng-transclude></button>
</script>

<div>
  <input type="text" ng-model="message" placeholder="请输入提示内容">
  <ts-message>固定</ts-message>
</div>
<script>
  var app = angular.module('myapp', []);
  app.directive('tsMessage', function () {
    return {
      restrict: 'EAC',
      templateUrl: 'tpl',
      transclude: true,
      scope: true,
      link: function (scope, element, attrs) {
        element.bind('click', function () {
          scope.$apply(function () {
            scope.message = '这是单击后的值';
          })
        })
      }
    }
  })
</script>

第二个例子:

html:

<div ng-controller="myCtrl">
  父亲: {{name}}
  <input ng-model="name">
  <div my-directive></div>
</div>
var app = angular.module('myApp', [])
  app.controller('myCtrl', ['$scope', function ($scope) {
    $scope.name = 'leifeng';
  }]);
  app.directive('myDirective', function () {
    return {
      restrict: 'EA',
      scope: true,  //父作用域发生改变,子作用域会变化,子作用域的变化不会影响父作用域
      template: '<div>儿子: {{name}}<input ng-model="name"></div>'
    }
  });

scope属性是对象

除了将scope属性设置为布尔值之外,还可以设置成一个JSON对象,如果是对象,那么父作用域与子作用域是完全独立的,不存在任何关联

app.directive('myDirective', function () {
  return {
    resrict: 'EA',
    scope: {},  //父作用域与子作用域相互不影响,改变任意一方都不会改变另一方
    template: //....
  }
})

这时,子作用域中如果需要添加属性,则可以通过link函数,在scope上添加,如果子作用域需要与调用父作用域的属性和方法,则需要在这个JSON对象中添加绑定策略

在JSON对象中添加的有3种绑定策略,分别是@=&

(1) @绑定

如果父作用域的属性内容修改了,子作用域对应的属性内容也会随之修改,而如果子作用域属性内容修改了,是不会影响父作用域对应的属性内容的。

html:

<div ng-controller="myCtrl">
  父亲: {{name}}
  <input ng-model="name">
  <my-directive name="{{name}}"></my-directive>
</div>

javascript:

<script>
  var app = angular.module('myApp', [])
  app.controller('myCtrl', ['$scope', function ($scope) {

  }]);
  app.directive('myDirective', function () {
    return {
      restrict: 'EA',
      scope: {
        name: '@'  //指令作用域中变量与父作用域中一致,直接使用@绑定
      },
      replace: true,
      template: '<div>儿子: {{name}}<input ng-model="name"></div>'
    }
  });
</script>

(2) =绑定

=绑定的功能是创建一个父作用域与子作用域可以同时共享的属性,即父作用域修改了该属性,子作用域也随之改变,反之亦然。

html:

<div ng-controller="myCtrl">
  <input type="text" ng-model="color" placeholder="Enter a color">
  {{color}}
  <hello-world color='color'></hello-world>
   <!--注意上面自定义指令里属性的写法-->
</div>

javascript:

<script>
  var app = angular.module('myApp', []);
  app.controller('myCtrl', ['$scope', function ($scope) {}]);
  app.directive('helloWorld', function () {
    return {
      restrict: 'EA',
      replace: true,
      scope: {
        color: '='
      },
      template: '<div style="background-color:{{color}}">Hello World<input type="text" ng-model="color"></div>'
    }
  });
</script>

(3) &绑定

&绑定的功能可以在独立的子作用域中直接调用父作用域的方法,在调用时可以向函数传递参数。

<div ng-controller="myCtrl">
  <input type="text" ng-model="name" placeholder="Eneter a color">
  {{name}}
  <hello-world saysomething999="say();" name="kaindy"></hello-world>
</div>

javascript:

<script>
  var app = angular.module('myApp', []);

  app.controller('myCtrl', ['$scope', function ($scope) {
    $scope.say = function () {
      alert('hello');
    }
    $scope.name = 'leifeng';
  }]);

  app.directive('helloWorld', function () {
    return {
      restrict: 'EA',
      replace: true,
      scope: {
        saysomething: '&saysomething999',
        name: '@'
      },
      template: '<button type="button" ng-bind="name" ng-init="saysomething();"></button>'
    }
  });
</script>

Angular指令对象的require和controller属性

requirecontroller两个属性常用于多个自定义指令元素嵌套时,即当一个子元素指令需要与父元素指令通信时,就需要使用这两个属性

require和controller属性的概念

require属性在创建子元素指令时添加,它的属性值用于描述与父元素指令通信时的方式,如^符号表示向外层寻找指定名称的指令,?符号表示即使没有找到,也不会出现异常

require: "^?myDirective"

controller属性值是一个构造函数,在创建父元素指令时添加,并可以在函数中创建多个属性或方法。在添加后,这些属性和方法都会被实例的对象所继承,而这个实例对象则是子元素指令中link函数的第4个参数

也就是说,当在子元素指令中添加了require属性,并通过属性值指定父元素指令的名称,那么就可以通过子元素指令中link函数的第4个参数来访问父元素指令中controller属性添加的方法。

controller: function () {
  this.a = function (childDirective) {
    // 方法a的函数体
  }
}
  • controller的属性值对应一个构造函数
  • this代表父元素指令本身
  • a表示构造函数中的一个任意的方法
  • childDirective形参表示子元素指令中的scope对象

html:

<div>
  <!--父元素指令-->
  <ts-parent>
    <div>{{ptip}}</div>
    <!--子元素指令-->
    <ts-child>
      <div>{{ctip}}</div>
    </ts-child>
    <button ng-click="click()">换位</button>
  </ts-parent>
</div>

javascript:

<script>
  var app = angular.module('myApp', []);

  app.directive('tsParent', function () {
    return {
      restrict: 'EA',
      controller: function ($scope, $compile, $http) {
        this.addChild = function (c) {
          $scope.ptip = '今天天气不错!';
          $scope.click = function () {
            $scope.tmp = $scope.ptip;
            $scope.ptip = c.ctip;
            c.ctip = $scope.tmp;
          }
        }
      }
    }
  });

  app.directive('tsChild', function () {
    return {
      restrict: 'EA',
      // 与父元素指令tsParent进行通信
      require: '^?tsParent',
      // 第4个参数表示父元素指令本身,可以调用定义在其上的方法
      link: function (scope, element, attrs, ctrl) {
        scope.ctip = '气温正好18摄氏度';
        ctrl.addChild(scope);
      }
    }
  });
</script>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容