指令<AngularJs>

指令<AngularJs>

对于指令。能够把它简单的理解成在特定DOM元素上执行的函数,指令能够扩展这个元素的功能。

首先来看个完整的參数演示样例再来具体的介绍各个參数的作用及使用方法:

复制代码
angular.module('myApp', []) 
.directive('myDirective', function() { 
    return { 
        restrict: String, 
        priority: Number, 
        terminal: Boolean, 
        template: String or Template Function: 
    function(tElement, tAttrs) {...}, 
    templateUrl: String, 
    replace: Boolean or String, 
    scope: Boolean or Object, 
    transclude: Boolean, 
    controller: String or 
    function(scope, element, attrs, transclude, otherInjectables) { ... }, 
    controllerAs: String, 
    require: String, 
    link: function(scope, iElement, iAttrs) { ... }, 
    compile: // 返回一个对象或连接函数,例如以下所看到的:
    function(tElement, tAttrs, transclude) { 
        return { 
            pre: function(scope, iElement, iAttrs, controller) { ... }, 
            post: function(scope, iElement, iAttrs, controller) { ... } 
           } 
        return function postLink(...) { ... } 
        } 
    }; 
 });
复制代码

restrict[string]

restrict是一个可选的參数。用于指定该指令在DOM中以何种形式被声明。默认值是A,即以属性的形式来进行声明。


可选值例如以下:
E(元素)

<my-directive></my-directive> 

A(属性,默认值)

<div my-directive="expression"></div> 

C(类名)

<div class="my-directive:expression;"></div> 

M(凝视)

<--directive:my-directive expression-->

一般考虑到浏览器的兼容性,强烈建议使用默认的属性就能够即即以属性的形式来进行声明。最后一种方式建议再不要求逼格指数的时候千万不要用。

Code:

复制代码
 angular.module('app',[])
    .directive('myDirective', function () {
            return { 
                restrict: 'E', 
                template: '<a href="http://www.baidu.com">百度</a>' 
            };
        })
HtmlCode:
 <my-directive></my-directive>
复制代码

 效果:

priority[int]

大多数指令会忽略这个參数,使用默认值0。但也有些场景设置高优先级是很重要甚至是必须的。比如,ngRepeat将这个參数设置为1000。这样就能够保证在同一元素上,它总是在其它指令之前被调用。

terminal[bool]

这个參数用来停止执行当前元素上比本指令优先级低的指令。但同当前指令优先级同样的指令还是会被执行。
比如:ngIf的优先级略高于ngView(它们操控的实际就是terminal參数)。假设ngIf的表达式值为true。ngView就能够被正常运行,但假设ngIf表达式的值为false,因为ngView的优先级较低就不会被运行。

template[string or function]

template參数是可选的,必须被设置为下面两种形式之中的一个:

  •  一段HTML文本;
  • 一个能够接受两个參数的函数,參数为tElement和tAttrs,并返回一个代表模板的字符串。tElement和tAttrs中的t代表template,是相对于instance的。

首先演示下另外一种使用方法:

复制代码
angular.module('app',[])
    .directive('myDirective', function () {
            return { 
                restrict: 'EAC', 
                template: function (elem, attr) {
                    return "<a href='" + attr.value + "'>" + attr.text + "</a>";
                }
        };
    })
复制代码

HtmlCode:(效果同上,不做演示了)

<my-directive value="http://www.baidu.com" text="百度"></my-directive>
        <div my-directive
             value="http://www.baidu.com"
              text="百度"></div>

templateUrl[string or function]

templateUrl是可选的參数,能够是下面类型:

  • 一个代表外部HTML文件路径的字符串。
  • 一个能够接受两个參数的函数,參数为tElement和tAttrs,并返回一个外部HTML文件路径的字符串。

不管哪种方式。模板的URL都将通过ng内置的安全层。特别是$getTrustedResourceUrl,这样能够保护模板不会被不信任的源载入。 默认情况下。调用指令时会在后台通过Ajax来请求HTML模板文件。

载入大量的模板将严重拖慢一个client应用的速度。为了避免延迟。能够在部署应用之前对HTML模板进行缓存。

Code:

复制代码
    angular.module('app',[])
    .directive('myDirective', function () {
            return { 
                restrict: 'AEC', 
                templateUrl: function (elem, attr) {
                    return attr.value + ".html";  //当然这里我们能够直接指定路径。同一时候在模板中能够包括表达式
                }
        };
    })
复制代码

replace[bool]

replace是一个可选參数,假设设置了这个參数,值必须为true,由于默认值为false。默认值意味着模板会被当作子元素插入到调用此指令的元素内部,
比如上面的演示样例默认值情况下,生成的html代码例如以下:

<my-directive value="http://www.baidu.com" text="百度"><a href="http://www.baidu.com">百度</a></my-directive>

假设设置replace=true

<a href="http://www.baidu.com" value="http://www.baidu.com" text="百度">百度</a>

据我观察,这样的效果仅仅有设置restrict="E"的情况下,才会表现出实际效果。

介绍完主要的指令參数后,就要涉及到更关键的数据域參数了...

scope參数[bool or object]

 scope參数是可选的,能够被设置为true或一个对象。

默认值是false。

假设一个元素上有多个指令使用了隔离作用域,当中仅仅有一个能够生效。仅仅有指令模板中的根元素能够获得一个新的作用域。因此,对于这些对象来说scope默认被设置为true。

内置指令ng-controller的作用,就是从父级作用域继承并创建一个新的子作用域。

它会创建一个新的从父作用域继承而来的子作用域。这里的继承就不在赘述,和面向对象中的继承基本是一直的。

 首先我们来分析一段代码:

复制代码
       <div ng-app="app" ng-init="name= '祖父'">
            <div ng-init="name='父亲'">
                第一代:{{ name }}
                <div ng-init="name= '儿子'" ng-controller="SomeController">
                    第二代: {{ name }}
                    <div ng-init="name='孙子'">
                        第三代: {{ name }}
                    </div>
                </div>
            </div>
        </div> 
复制代码

我们发现第一代。我们初始化name为父亲,可是第二代和第三代事实上是一个作用域,那么他们的name事实上是一个对象。因此出现的效果例如以下:

第一代:父亲
第二代: 孙子
第三代: 孙子

我们在改动一下代码。把第三代隔离开来再看看效果:

复制代码
 <div ng-app="app"ng-init="name= '祖父'">
            <div ng-init="name='父亲'">
                第一代:{{ name }}
                <div ng-init="name= '儿子'" ng-controller="SomeController">
                    第二代: {{ name }}
                    <div ng-init="name='孙子'" ng-controller="SecondController">
                        第三代: {{ name }}
                    </div>
                </div>
            </div>
        </div>
复制代码

JsCode:

复制代码
 angular.module('app', [])
        .controller('SomeController',function($scope) {
            
        })
       .controller('SecondController', function ($scope) {
        
    }) 
复制代码

效果例如以下:

第一代:父亲
第二代: 儿子
第三代: 孙子

在改动下代码来看看继承:

复制代码
       <div ng-app="app"ng-init="name= '祖父的吻'">
            <div>
                第一代:{{ name }}
                <div ng-controller="SomeController">
                    第二代: {{ name }}
                    <div  ng-controller="SecondController">
                        第三代: {{ name }}
                    </div>
                </div>
            </div>
        </div> 
复制代码

 

效果例如以下:

第一代:祖父的吻
第二代: 祖父的吻
第三代: 祖父的吻

假设要创建一个可以从外部原型继承作用域的指令,将scope属性设置为true,简单来说就是可继承的隔离,即不能反向影响父作用域。

 再来看个样例:

复制代码
    angular.module('myApp', [])
        .controller('MainController', function ($scope) {
        })
        .directive('myDirective', function () {
            return {
                restrict: 'A',
                scope:false,//切换为{},true測试
                priority: 100,
                template: '<div>内部:{{ myProperty }}<input ng-model="myProperty"/></div>'
            };
        });
复制代码

Html代码:

 <div ng-controller='MainController' ng-init="myProperty='Hello World!'">
        外部: {{ myProperty}}
        <input ng-model="myProperty" />
        <div my-directive></div>
    </div>

 当我们改变scope的值我们会发现

false:继承但不隔离

true:继承并隔离

{}:隔离且不继承 

 transclude

transclude是一个可选的參数。默认值是false。嵌入通经常使用来创建可复用的组件,典型的样例是模态对话框或导航栏。

我们能够将整个模板。包含当中的指令通过嵌入所有传入一个指令中。指令的内部能够訪问外部指令的作用域,而且模板也能够訪问外部的作用域对象。为了将作用域传递进去,scope參数的值必须通过{}或true设置成隔离作用域。假设没有设置scope參数,那么指令内部的作用域将被设置为传入模板的作用域。

仅仅有当你希望创建一个能够包括随意内容的指令时。才使用transclude: true。

 我们来看两个样例-导航栏:

复制代码
    <div side-box title="TagCloud">
        <div class="tagcloud">
            <a href="">Graphics</a>
            <a href="">ng</a>
            <a href="">D3</a>
            <a href="">Front-end</a>
            <a href="">Startup</a>
        </div>
   </div>
复制代码

JsCode:

复制代码
 angular.module('myApp', []) 
 .directive('sideBox', function() { 
     return { 
         restrict: 'EA', 
         scope: { 
             title: '@' 
         }, 
         transclude: true, 
         template: '<div class="sidebox"><div class="content"><h2 class="header">' +
             '{{ title }}</h2><span class="content" ng-transclude></span></div></div>' 
         }; 
    }); 
复制代码

这段代码告诉ng编译器。将它从DOM元素中获取的内容放到它发现ng-transclude指令的地方。

再来你看个官网的样例:

复制代码
angular.module('docsIsoFnBindExample', [])
  .controller('Controller', ['$scope', '$timeout', function($scope, $timeout) {
    $scope.name = 'Tobias';
    $scope.hideDialog = function () {
      $scope.dialogIsHidden = true;
      $timeout(function () {
        $scope.dialogIsHidden = false;
      }, 2000);
    };
  }])
  .directive('myDialog', function() {
    return {
      restrict: 'E',
      transclude: true,
      scope: {
        'close': '&onClose'
      },
      templateUrl: 'my-dialog-close.html'
    };
  });
复制代码
my-dialog-close.html
<div class="alert">
  <a href class="close" ng-click="close()">&times;</a>
  <div ng-transclude></div>
</div>

index.html

<div ng-controller="Controller">
  <my-dialog ng-hide="dialogIsHidden" on-close="hideDialog()">
    Check out the contents, {{name}}!
  </my-dialog>
</div>

假设指令使用了transclude參数,那么在控制器无法正常监听数据模型的变化了。建议在链接函数里使用$watch服务。

 controller[string or function]

 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) { 
// 控制器逻辑放在这里
} 
}); 
复制代码

我们能够将随意能够被注入的ng服务注入到控制器中,便能够在指令中使用它了。控制器中也有一些特殊的服务能够被注入到指令其中。

这些服务有:

1. $scope

与指令元素相关联的当前作用域。
2. $element
当前指令相应的元素。


3. $attrs
由当前元素的属性组成的对象。

 

<div id="aDiv"class="box"></div>
具有例如以下的属性对象:
{ 
id: "aDiv", 
class: "box" 
} 

4. $transclude
嵌入链接函数会与相应的嵌入作用域进行预绑定。transclude链接函数是实际被运行用来克隆元素和操作DOM的函数。

复制代码
  angular.module('myApp',[])
 .directive('myLink', function () {
     return {
         restrict: 'EA',
         transclude: true,
         controller:
         function ($scope, $element,$attrs,$transclude) {
             $transclude(function (clone) {              
                 var a = angular.element('<a>');
                 a.attr('href', $attrs.value);
                 a.text(clone.text());
                 $element.append(a);
             });
         }
     };
 });
复制代码

html

    <my-link value="http://www.baidu.com">百度</my-link>
    <div my-link value="http://www.google.com">谷歌</div>

仅在compile參数中使用transcludeFn是推荐的做法。link函数能够将指令互相隔离开来,而controller则定义可复用的行为。假设我们希望将当前指令的API暴露给其它指令使用,能够使用controller參数,否则能够使用link来构造当前指令元素的功能性(即内部功能)。

假设我们使用了scope.$watch()或者想要与DOM元素做实时的交互,使用链接会是更好的选。使用了嵌入,控制器中的作用域所反映的作用域可能与我们所期望的不一样,这样的情况下。$scope对象无法保证能够被正常更新。当想要同当前屏幕上的作用域交互时,能够使用传入到link函数中的scope參数。

controllerAs[string]

controllerAs參数用来设置控制器的别名,这样就能够在视图中引用控制器甚至无需注入$scope。

<div ng-controller="MainController as main">
        <input type="text" ng-model="main.name" />
        <span>{{ main.name }}</span>
    </div> 

JsCode:

  angular.module('myApp',[])
   .controller('MainController', function () {
       this.name = "Halower";
   });

控制器的别名使路由和指令具有创建匿名控制器的强大能力。这样的能力能够将动态的对象创建成为控制器。而且这个对象是隔离的、易于測试。

 require[string or string[]]

 require为字符串代表另外一个指令的名字。

require会将控制器注入到其所指定的指令中,并作为当前指令的链接函数的第四个參数。字符串或数组元素的值是会在当前指令的作用域中使用的指令名称。在不论什么情况下,ng编译器在查找子控制器时都会參考当前指令的模板。

  • 假设不使用^前缀,指令仅仅会在自身的元素上查找控制器。指令定义仅仅会查找定义在指令作当前用域中的ng-model=""
  • 假设使用?

    前缀,在当前指令中没有找到所须要的控制器,会将null作为传给link函数的第四个參数。

  • 假设加入了^前缀,指令会在上游的指令链中查找require參数所指定的控制器。
  •  假设加入了?^ 将前面两个选项的行为组合起来。我们可选择地载入须要的指令并在父指令链中进行查找
  • 假设没有不论什么前缀,指令将会在自身所提供的控制器中进行查找,假设没有找到不论什么控制器(或具有指定名字的指令)就抛出一个错误

compile【object or function】

compile选项本身并不会被频繁使用,可是link函数则会被常常使用。本质上,当我们设置了link选项,实际上是创建了一个postLink() 链接函数,以便compile() 函数能够定义链接函数。

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

compile和link选项是相互排斥的。

假设同一时候设置了这两个选项,那么会把compile所返回的函数当作链接函数,而link选项本身则会被忽略。

编译函数负责对模板DOM进行转换。链接函数负责将作用域和DOM进行链接。 在作用域同DOM链接之前能够手动操作DOM。

在实践中,编写自己定义指令时这种操作是很罕见的,但有几个内置指令提供了这种功能。

 

link

compile: function(tEle, tAttrs, transcludeFn) {
 //todo:
 return function(scope, ele, attrs) {
 // 链接函数
 };

链接函数是可选的。

假设定义了编译函数,它会返回链接函数,因此当两个函数都定义时。编译函数会重载链接函数。假设我们的指令非常easy,而且不须要额外的设置。能够从工厂函数(回调函数)返回一个函数来取代对象。假设这样做了,这个函数就是链接函数。

ngModel

它提供更底层的API来处理控制器内的数据,这个API用来处理数据绑定、验证、 CSS更新等不实际操作DOM的事情,ngModel 控制器会随 ngModel 被一直注入到指令中,当中包括了一些方法。为了訪问ngModelController必须使用require设置.

ngModelController经常使用的元素例如以下:

  •  1.为了设置作用域中的视图值,须要调用 ngModel.$setViewValue() 函数。

$setViewValue() 方法适合于在自己定义指令中监听自己定义事件(比方使用具有回调函数的jQuery插件),我们会希望在回调时设置$viewValue并运行digest循环。

复制代码
复制代码
 angular.module('myApp')
        .directive('myDirective', function() {
            return {
                require: '?

ngModel', link: function(scope, ele, attrs, ngModel) { if (!ngModel) return; $(function() { ele.datepicker({
//回调函数 onSelect: function(date) { // 设置视图和调用 apply scope.$apply(function() { ngModel.$setViewValue(date); }); } }); }); } }; });

复制代码
复制代码
    • 2.$render方法能够定义视图详细的渲染方式
    • 3.属性
      1. $viewValue
      $viewValue属性保存着更新视图所需的实际字符串。


      2. $modelValue
      $modelValue由数据模型持有。 $modelValue和$viewValue可能是不同的。取决于$parser流水线是否对其进行了操作。


      3. $parsers
      $parsers 的值是一个由函数组成的数组。当中的函数会以流水线的形式被逐一调用。

      ngModel 从DOM中读取的值会被传入$parsers中的函数,并依次被当中的解析器处理。
      4. $formatters
      $formatters的值是一个由函数组成的数组。当中的函数会以流水线的形式在数据模型的值发生变化时被逐一调用。

      它和$parser流水线互不影响。用来对值进行格式化和转换,以便在绑定了这个值的控件中显示。
      5. $viewChangeListeners
      $viewChangeListeners的值是一个由函数组成的数组,当中的函数会以流水线的形式在视图中的值发生变化时被逐一调用。

      通过$viewChangeListeners。能够在无需使用$watch的情况下实现类似的行为。因为返回值会被忽略,因此这些函数不须要返回值。
      6. $error
      $error对象中保存着没有通过验证的验证器名称以及相应的错误信息。
      7. $pristine
      $pristine的值是布尔型的,能够告诉我们用户是否对控件进行了改动。


      8. $dirty
      $dirty的值和$pristine相反。能够告诉我们用户是否和控件进行过交互。
      9. $valid
      $valid值能够告诉我们当前的控件中是否有错误。

      当有错误时值为false, 没有错误时值为true。
      10. $invalid
      $invalid值能够告诉我们当前控件中是否存在至少一个错误,它的值和$valid相反。

 未完待续....

     备注:我也是刚刚開始学习,假设你喜欢本文的话,推荐共勉,谢谢!


posted on 2017-07-11 12:02  wgwyanfs  阅读(174)  评论(0编辑  收藏  举报

导航