angularAngularJS实战笔记我爱编程

Angular指令

2016-08-23  本文已影响364人  阿振_sc

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个主要的参数:

<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的函数体
  }
}

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>
上一篇下一篇

猜你喜欢

热点阅读