AngularJS - 创建自定义的指令(directive)

除了 AngularJS 内置的指令外,我们还可以创建自定义指令。你可以使用 .directive 函数来添加自定义的指令。要调用自定义指令,HTML 元素上需要添加自定义指令名。

使用驼峰法来命名一个指令, runoobDirective, 但在使用它时需要以 - 分割,

内部有许多参数可以供使用。

内部参数

1:restrict 

我们自定义的指令可以使用四种方式调用。

  • 元素名     
  • 属性        
  • 类名     
  • 注释     
  • 而restrict属性就是用来限制其使用的。restrict 值可以是以下几种:

    • E 作为元素名使用
    • A 作为属性使用
    • C 作为类名使用
    • M 作为注释使用

    restrict 默认值为 EA, 即可以通过元素名和属性名来调用指令。

    2:priority 该属性用来规定指令的优先级,默认为0。 

     3:template : 与指令关联的HTML模板, 
    ·4:templateUrl :与指令关联的HTML模板路径, 
    ·5:replace :是否采用HTML模板替换原有的元素

    外部属性

    1:scope

    scope 参数的作用是:隔离指令与所在控制器(父级作用域的控制器)间的作用域,隔离指令与指令之间的作用域。 
    scope的值是可选的,可选值分别为:false,true,object,默认情况下为false; 
    ·false 共享父作用域 
    ·true 继承父作用域,并且新建独立作用域 
    ·object 不继承父作用域,创建新的独立作用域

    .

    scope取值为false时,指令和父作用域共同使用用同一个作用域 

    看如下代码:(在html标签上面加上了ng-app="myApp")

        
             
    
             
        
    
    
    (function() {
        angular.module('myApp',[]).controller('myControl',["$scope",function($scope) {
            $scope.fatherData='I am father!';
        }])
        .directive('inputSon' ,[function() {
            return {
                restrict:'EA',
                replace:true,
                scope:false,
                template:[
                    '',
                    ''
                ].join(''),
                controller:['$scope',function($scope) {
                    $scope.fatherData='I am son!';
                }]
    
            }
        }])
    }());

    效果截图:我对后面的标签进行修改,那么前面的标签会一起修改,因为他们的作用域相同

     

    scope取true值,那么指令就和父指令作用域的关系类似于原型链继承的方式,先在自己的作用域找,没有就找上一级。

    看如下代码:(在html标签上面加上了ng-app="myApp")

        
             
             
        

     

    ​
    (function() {
        angular.module('myApp',[]).controller('myControl',["$scope",function($scope) {
            $scope.fatherData='I am father!';
        }])
        .directive('inputSon' ,[function() {
            return {
                restrict:'EA',
                replace:true,
                scope:true,
                template:[
                    '',
                    '',
                    
                ].join(''),
                controller:['$scope',function($scope) {
                    $scope.fatherData='I am son!';//注销后,变成I am father!
                }]
    
            }
        }])
    }());
    
    ​

    当把子指令作用域内的fatherData注释后,那么就变成:

    因为在自己的作用域内已经找不到fatherData,那么就去父级找。

     

    scope取{} 时,那么就完全在一个全新的作用域,与父级没有关系(没有继承)

    看如下代码:(在html标签上面加上了ng-app="myApp")

        
             
             
        
    (function() {
        angular.module('myApp',[]).controller('myControl',["$scope",function($scope) {
            $scope.fatherData='I am father!';
        }])
        .directive('inputSon' ,[function() {
            return {
                restrict:'EA',
                replace:true,
                scope:{},
                template:[
                    '',
                    '',
                    
                ].join(''),
                controller:['$scope',function($scope) {
                    $scope.fatherData='I am father!';//注释掉后,那么就变成空了
                }]
    
            }
        }])
    }());

    如果把子指令作用域内的fatherData注释掉,那么就找不到值变成空

     

    当scope={ //里面有一些东西} (非空对象),指令会将该对象处理成子域scope的扩展属性。这一扩展属性肩负起了指令和父作用域通信的任务。 

    @ || @attr:单向绑定  子可以感受到父的变化,反之不行    = || =attr:双向绑定    & :从而以函数的方式读写父作用域的属性

    使用@时,属性赋值需要用{{}}表达式!!!

    使用&时,一定需要用on开头!!!

    看如下代码:(在html标签上面加上了ng-app="myApp")

        
             
             
              
             
    (function() {
        angular.module('myApp',[]).controller('myControl',["$scope",function($scope) {
            $scope.fatherData='I am father!';
            $scope.fatherName='XXX';
            $scope.speak=function() {
                console.log("汪汪汪");
            }
        }])
        .directive('inputSon' ,[function() {
            return {
                restrict:'EA',
                replace:true,
                scope:{
                    myData:'@',
                    myName:'=',
                    onSpeak:'&'
                },
                template:[
                    '
    ', '', '', '', '
    ' ].join(''), controller:['$scope',function($scope) { }] } }]) }());

    我们依次改变四个框里面的内容,从左到右,从上到下

     

    AngularJS - 创建自定义的指令(directive)_第1张图片子可以感受到父级的变化,没毛病

    AngularJS - 创建自定义的指令(directive)_第2张图片双向绑定的'='就更加不用说了。

    AngularJS - 创建自定义的指令(directive)_第3张图片子级的内容已经改变了,而父级感受不到,没毛病

    没毛病

    点击俩个按钮都是有用的,输出"汪汪汪"

    现在我们在子级的控制器里面加上一些属性,会影响输出结果吗?

    没有变化,与父级是隔离的。

     

    2:require

    引入其他指令并注入到控制器中,并作为当前指令的链接函数的第四个参数。require使用字符串或数组元素来传入指令。用通俗的话说require字段,作用是用于指令之间的相互交流。如果是数组,注入的参数是一个相应顺序的数组。如果这样的指令没有被找到,或者该指令没有控制器, 就会报错。 require参数可以加一些前缀:

    • (没有前缀)如果没有前缀,指令将会在自身所提供的控制器中进行查找,如果没有找到任何控制器就抛出一个错误。
    • ? 如果在当前指令中没有找到所需要的控制器,会将null作为传给link函数的第四个参数。
    • ^ 如果添加了^前缀,指令会在上游的指令链中查找require参数所指定的控制器。
    • ?^ 将前面两个选项的行为组合起来,我们可选择地加载需要的指令并在父指令链中进行查找。

    看如下代码:(在html标签上面加上了ng-app="myApp")

        
            
    (function() {
        angular.module('myApp',[]).controller('myControl',['$scope',function($scope) {
    
        }])
        .directive('father',[function() {
            return {
                scope:{},
                replace:true,
                restrict:'EA',
                template:[
                   
                ].join(''),
                controller:['$scope', function($scope) {
                    var data=['1','2','3','4'];//定义一个数组
                    this.click=function() {//定义一个函数,每一次点击就抛出一个数
                        data.pop();
                        console.log(data);
                    }
                }]
            }
        }]) 
        .directive('son',[function() {
            return {
                scope:{},
                replace:true,
                restrict:'EA',
                template:[
                    '点一下'
                ].join(''),
                require:'?^father',
                link:function($scope,elem,attr,ctrl) {
                    $scope.sonClick=ctrl.click;//引用那个点击函数,
                }
            }
        }])
    }());

     AngularJS - 创建自定义的指令(directive)_第4张图片效果如图。

    假如我们现在需要编写两个指令,在linking函数中有很多重合的方法,为了避免重复,我们可以将这个重复的方法写在第三个指令的 controller中,然后在另外两个需要的指令中require这个拥有controller字段的指令,最后通过linking函数的第四个参数就可以引用这些重合的方法。

     

    行为参数link与controller

    link与controller参数都是描述指令行为的参数,但他们两分别负责不同的行为描述。 
    controller关注的是指令自身内部作用域具备什么样的行为,其关注点在于指令作用域的行为上。 
    link关注的是指令中HTML模板的操作行为,其关注点在于DOM操作行为上,这个属性只有在compile这个属性未定义时才能使用,.以编程方式修改生成的DOM元素实例,添加事件监听器,设置数据绑定。

     

     

    link 和 compile (其中link又可以分成pre-link 和post-link)

    看如下代码:(在html标签上面加上了ng-app="myApp")

    
            
                
                    22333
                
            
    
    (function() {
        angular.module('myApp',[]) 
        .directive('father',[function() {
            return {
                restrict:'EA',
                compile:function(tElem,tAttrs) {
                    console.log('Fa Complie Now');
                    return  {
                        pre:function(scope,elem,attr) {
                            console.log('Fa prelink now');
                        },
                        post:function(scope,elem,attr) {
                            console.log('Fa postlink now');
                        }
                    }
                      
                    
                } 
            }
        }])
        .directive('son',[function() {
            return {    
                restrict:'EA',
                compile:function(tElem,tAttrs) {
                    console.log('Son Complie Now');
                    return {
                        pre:function(scope,elem,attr) {
                            console.log('Son prelink now');
                        },
                        post:function(scope,elem,attr) {
                            console.log('Son postlink now');
                        }
                    }
                }
            }
        }])
    }())

    我们来简单看看那个俩个指令的过程

    AngularJS - 创建自定义的指令(directive)_第5张图片执行过程非常清楚了,我们看到先进行compile 然后在进行prelink 最后以栈的顺序(后进先出的顺序)执行postlink函数。

    compile过程:在编译的阶段,angularJs会遍历整个的文档并根据JavaScript中指令定义来处理页面上什么的指令。在遍历的过程中,有可能一层套着一层,一直延深处遍历。一但遍历和编译完毕就会返回一个叫做模板函数的函数。在这个函数没被返回(return)之前我们可以对编译后的DOM树进行修改。这个原始的dom被称为template element,这个原始的element会被当成参数传给compile函数,在compile函数后,提供一个scope对象,这个对象有可能是父级的,也有可能是自己的,取决于你的scope属性的值。我们对这个原始的dom进行操作是安全的。还记得ng-repeat吗,它增加了dom节点,就是在这个地方操作的。

    post-link函数:本质上,当我们设置了link选项,实际上是创建了一个postLink() 链接函数。

    刚刚说了是栈的顺序(先进后出)执行postlink函数,也就是说当执行父级的postlink函数时,子级的postlink函数一定都是执行完了的。一般将逻辑写到这里是很安全的。

    刚刚说了compile过后,生成了一个scope对象,这个对象在linking阶段是可以用的,已经传到参数列表里面了。

    pre-link函数: 在compile之后,但是在post-link之前,可以将这一些逻辑业务写到pre-link函数里面。

    注意这里的父级pre-link函数一定在子级pre-link函数之前执行,和上面相反。

    //link函数主要用于操作dom元素,给dom元素绑定事件和监听.
    link:function($scope,element,attr,ctrl,linker){
    	//$scope:指令所在的作用域,有关上下文
    	//element:指令元素的封装,可以调用angular封装的简装jq方法和属性
    	//attr:指令元素的属性的集合
    	//ctrl:用于调用其他指令的方法,指令之间的互相通信使用,需要配合require,刚刚演示了
    	//linker:用于transClude里面嵌入的内容
    }
    
    
    compile:function (tElem,tAttrs) {
        //
        return {
            pre:function(scope,elem,attr) {
                //
            },
            post:function(scope,elem,attr) {
               //
            }
        }
    }

     

     

    你可能感兴趣的:(AngularJS)