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

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

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

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

内部参数

1:restrict 

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

  • 元素名     <runoob-directive></runoob-directive>
  • 属性        <div runoob-directive></div>
  • 类名     <div class="runoob-directive"></div>
  • 注释     <!-- directive: runoob-directive -->

而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")

    <body ng-controller='myControl'>
         <input ng-model='fatherData'>

         <input-son></input-son>
    </body>

(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:[
                '<input ng-model="fatherData">',
                '</input>'
            ].join(''),
            controller:['$scope',function($scope) {
                $scope.fatherData='I am son!';
            }]

        }
    }])
}());

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

 

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

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

    <body ng-controller='myControl'>
         <input ng-model='fatherData'>
         <input-son></input-son>
    </body>

 

​
(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:[
                '<input ng-model="fatherData">',
                '</input>',
                
            ].join(''),
            controller:['$scope',function($scope) {
                $scope.fatherData='I am son!';//注销后,变成I am father!
            }]

        }
    }])
}());

​

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

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

 

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

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

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

        }
    }])
}());

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

 

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

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

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

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

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

    <body ng-controller='myControl'>
         <input ng-model='fatherData'>
         <input ng-model='fatherName'>
         <button class='btn btn-success btn-lg' ng-click='speak'>点一下</button> 
         <br>
         <input-son my-data='{{fatherData}}' my-name='fatherName' on-speak='speak()' ></input-son>
         <!-- myData:'@' 所以用{{fatherData}} -->
         <!-- onSpeak -->
    </body>
(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:[
                '<div>',
                  '<input ng-model="myData"></input>',
                  '<input ng-model="myName"></input>',
                  '<button ng-click="onSpeak()" class="btn btn-info btn-lg">点一下</button>',
                '</div>'
                
            ].join(''),
            controller:['$scope',function($scope) {
                 
            }]

        }
    }])
}());

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

 

子可以感受到父级的变化,没毛病

双向绑定的'='就更加不用说了。

子级的内容已经改变了,而父级感受不到,没毛病

没毛病

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

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

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

 

2:require

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

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

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

    <body  ng-controller='myControl'>
        <div class="container" >
             <father>
                    <son></son>
             </father>
        </div>
    </body>
(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:[
                '<buttn  class="btn btn-success btn-lg" ng-click="sonClick()">点一下</button>'
            ].join(''),
            require:'?^father',
            link:function($scope,elem,attr,ctrl) {
                $scope.sonClick=ctrl.click;//引用那个点击函数,
            }
        }
    }])
}());

 效果如图。

假如我们现在需要编写两个指令,在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")

<body >
        <father>
            <son>
                22333
            </son>
        </father>
</body>
(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');
                    }
                }
            }
        }
    }])
}())

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

执行过程非常清楚了,我们看到先进行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) {
           //
        }
    }
}

 

 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在uni-app中,可以通过自定义指令来实现自适应DPR加载图片。具体步骤如下: 1. 在项目的`main.js`中注册自定义指令: ```javascript import Vue from 'vue'; import App from './App'; Vue.config.productionTip = false; // 注册自定义指令 Vue.directive('adapt-img', { bind: function (el, binding) { let dpr = window.devicePixelRatio; let value = binding.value; if (value) { if (dpr >= 3 && value.indexOf('@3x') !== -1) { el.setAttribute('src', value.replace('@3x', '')); } else if (dpr >= 2 && value.indexOf('@2x') !== -1) { el.setAttribute('src', value.replace('@2x', '')); } else { el.setAttribute('src', value); } } }, }); App.mpType = 'app'; const app = new Vue({ ...App, }); app.$mount(); ``` 这里注册了一个名为`adapt-img`的自定义指令,当该指令绑定到元素上时,会根据当前设备的DPR加载对应的图片。 2. 在需要使用自适应DPR加载图片的地方,将自定义指令`adapt-img`绑定到`<img>`标签上,并设置图片路径: ```html <template> <div class="demo"> <img v-adapt-img src="@/static/img/demo@3x.png" /> </div> </template> <style> .demo img { width: 100px; height: 100px; } </style> ``` 这里将自定义指令`adapt-img`绑定到`<img>`标签上,并设置图片路径为`@/static/img/demo@3x.png`。在绑定指令时,可以将图片路径作为指令的值传递过去。 3. 在图片文件夹中,只需要提供@2x和@3x倍图即可,不需要提供@1x倍图。 这样,当页面加载时,自定义指令会根据当前设备的DPR加载对应的图片,如果当前设备的DPR为1,则会加载原始图片。 需要注意的是,如果图片文件名中包含了`@2x`或`@3x`,需要在指令中进行判断并替换。如果图片文件名中没有包含`@2x`或`@3x`,则直接加载原始图片。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值