2.3.4 AngularJS 自定义指令 - OhNaNaSun/angularBlog GitHub Wiki

指令是如何被编译的

👍 AngularJS指令编写实用指南(一)

compile阶段进行标签解析和变换,link阶段进行数据绑定等操作.

当应用在启动时,Angular开始使用$compile服务解析DOM。这项服务会在标记中寻找指令然后将它们各自匹配到注册的适龄。一旦所有的指令都已经被识别完成,Angular就开始执行它们的compile函数。正如前面所提到的,compile函数返回一个link函数,该函数会被添加到稍后执行的link函数队列中。这叫做编译阶段(compile phase)。注意到即使同一个指令有几个实例存在,compile函数也只会运行一次。

在编译阶段之后就到了链接阶段(link phase),这时link函数就一个接一个的执行。在这个阶段中模板被生成,指令被运用到正确的作用域,DOM元素上开始有了事件监听器。不像是compile函数,lin函数会对每个指令的实例都执行一次。

改变指令的作用域

默认情况下指令应该访问父作用域。但是我们并不像对所有情况一概而论。如果我们对指令暴露了父控制器的scope,那么指令就可以自由的修改scope属性。在一些情况下你的指令可能想要添加一些只有内部可以使用的属性和函数。如果我们都在父作用域中完成,可能会污染了父作用域。因此,我们有两种选择:

  • 一个子作用域 - 这个作用域会原型继承父作用域。
  • 一个隔离的作用域 - 一个全新的、不继承、独立存在的作用域。

👍 AngularJS指令开发(1)——参数详解

对外参数——require

scope是指令与外界作用域通讯的桥梁,而require是指令与指令之间通讯的桥梁。这个参数最大的作用在于,当要开发单指令无法完成,需要一些组合型指令的控件或功能,例如日期控件,通过require参数,指令可以获得外部其他指令的控制器,从而达到交换数据、事件分发的目的。

使用方法:require: String or Array——String值为引入指令名称,并且有两个寻找指令策略符号‘?’与‘^’;Array数组则为多个外部指令名称。

既然require可以获取外部指令,那Angular原生指令应该也是能够获取。其中最广泛应用的就是require: 'ngModel'. 在实际应用时,我们往往会require 'ngModel',也就是希望利用AngularJs内置指令ngModel里的方法,而不是自己重新写.

行为参数——link与controller: link

简单来说就是:

  1. 加载模板,形成DOM模板树
  2. @@@@
  3. 数据绑定 @@@@是啥?没错,就是link链接函数,它会在形成模板树之后,在数据绑定之前,从最底部指令开始,逐个指令执行它们的link函数。

在这个时间节点的link函数,操作DOM的性能开销是最低,非常适合在这个时机执行DOM的操作,例如鼠标操作或触控事件分发绑定、样式Class设置、增删改元素等等。

所以link就是描述指令元素操作行为。

link函数的参数

AngularJS指令编写实用指南(一) 注意到link函数被用在了指令中。它接收三个参数:

  • scope - 它代表指令被使用的作用域。在上面的例子中它等同于符控制器的作用域。
  • elem - 它代表绑定指令的元素的jQlite(jQuery的一个自己)包裹元素。如果你在AngularJS被包含之前就包括了jQuery,那么它将变成jQuery包裹元素。由于该元素已经被jQuery/jQlite包裹,我们没有必要将它包含在$()中来进行DOM操作。
  • attars - 它代表绑定指令的元素上的属性。例如,如果你在HTML元素上有一些指令形式为:<hello-world some-attribute></hello-world>,你可以在link函数内用attrs.someAttribute来引用这些属性。 link函数主要是用来对DOM元素绑定事件监听器,监视模型属性变化,并更新DOM。

指令与ngModel指令的交互

angular指令中使用ngModelController

官方API

深入了解Angularjs指令中的ngModel

AngularJs-$parsers自我理解-解析

$parsers

首先先了解下它具体的作用,当用户与控制器进行交互的时候。ngModelController中的$setViewValue()方法就会被调用,$parsers的数组中函数就会以流水线的形式被一一调用。第一个$parse被调用后在进行第二个$parse调用

这些函数可以对输入值进行转换,后者通过$setValidity()函数设置表达的合法性: 这个方法可以人为的设置一个表单控件的$valid 以及$ invalid, 也就是说改变表单控件是否通过校验的状态.类似的还有$setDirty()和$setPristine().

那么具体是实现,用了一些时间做了一个突击测试,也没有花什么时间研究,有不妥也正常,但应该也差不多了 首先$parsers是view 到 model的一个过度,因此当Model不希望有所更新的时候就返回一个undefined。

一个数组.数组里的元素是函数. $setViewValue(value)被赋值给$modelValue之前,value值首先会经过$parsers里的所有函数,每次将返回值传递给下一个函数.最后才被赋值到$modelValue.在这个过程中就包括了验证和转换.对于验证这个步骤,它会使用$setValidity这个方法,验证失败的将返回undefined.

$setValidity

来设置错误的标志

为一个函数,接受两个参数,第一个参数为错误标志的名字,是字符串类型,将会被设置成$error的属性

第二个参数为布朗值,为这个错误标志的值。

我们在控制台中打印出来ngmodel.$setValidity

用通俗的话讲,用法就是ngmodel.$setValidity('flag',布朗值)

这样我们就可以在页面上用formname.inputname.$error.flag

用法

  <form name="myForm">
      <input name="story" type="number" ng-model="story.point" class="spoint" required/>    
      <span ng-show="myForm.repeatpassword.$error.compare">not legal points</span>
  </form>
app.directive('compare', function(){
        return {
            require: "ngModel",
            link: function(scope, elm, attrs, ngmodel){
                // console.log(ngmodel);
                ngmodel.$parsers.unshift(function(viewValue){
                    if(viewValue == "" || attrs.compare == "" || viewValue == attrs.compare){
                        ngmodel.$setValidity('compare', true)
                    } else {
                        ngmodel.$setValidity('compare', false)
                    }
                    return viewValue;
                })
            }
        }
    })
⚠️ **GitHub.com Fallback** ⚠️