Angular自定義指令

在Angular當(dāng)中,指令是可以用來實(shí)現(xiàn)你想做的任何事情的利器,它可以給Dom綁定你所指定的行為(例如事件監(jiān)聽、添加點(diǎn)擊事件),甚至是它都可以直接轉(zhuǎn)換Dom元素(例如直接將<div>標(biāo)簽轉(zhuǎn)換成一個列表)。

而在原生的Angular中自帶了很多的指令,例如ngModel、ngClass、ngRepeat等等。但有時(shí)官方的指令滿足不了我們的需求,這時(shí)就需要創(chuàng)建特定的指令,例如當(dāng)前項(xiàng)目中的手機(jī)號限制指令、富文本字?jǐn)?shù)限制指令、模糊搜索指令等等。而且,指令可以減少重復(fù)邏輯代碼的編寫,方便使用,提升開發(fā)效率。所以,學(xué)習(xí)下如何自定義指令是很有必要的。

那么我們?nèi)绾巫远x指令?

指令的一般模板

var myModule = angular.module([]);myModule.directive('myDirective', function() {    return {        restrict: 'EA',        priority: 1000,        template: '<div></div>',        templateUrl: 'test.html',        replace: true,        transclude: true,        scope: true,        controller: function ($scope, $element, $attrs, $transclude) {                    },        require: 'ngRepeat',        link: function (scope, iElement, iAttrs) {        }    };});

它里面有很多屬性,每個屬性的作用如下圖:

image.png

實(shí)現(xiàn)一個指令

第一個指令,helloWorld

實(shí)現(xiàn)指令的代碼如下:

demoApp.directive('helloWorld', function () {
    return {
        restrict: 'E',
        template: '<h4>Hello world</h4>'
    }
});

當(dāng)實(shí)現(xiàn)之后,我們就可以在我們的html中使用helloWorld指令了,如下:

<div>
  <hello-world></hello-world>
</div>

在上面的helloWorld指令中,我們用到下面一些內(nèi)容:

指令命名

在指令的定義中使用的是駝峰式命名,在模板使用時(shí),使用
的是通過短橫線連接的。主要是能夠支持HTML校驗(yàn)規(guī)則,我
們這里使用的是H5的HTML規(guī)則。

例如,上面的helloWorld指令,我們在定義指令的時(shí)候,使用的是hellWorld這種駝峰式,而我們在模板中使用指令的時(shí)候是<hello-world></hello-world>,使用的是用短橫線連接。

restrict屬性

它描述指令的聲明風(fēng)格,即它是否可以作元素名稱、元素屬性樣式類或者注釋。

image.png

注意:當(dāng)想讓一個指令可以作為元素,也可以作為屬性時(shí),可以使用“AE”

例如,上面的helloWolrd指令,只是作為一個元素,因?yàn)樗膔estrict為E。

模板

模板有兩種形式:

  • template:以字符串形式編寫的一個內(nèi)聯(lián)模板
  • templateUrl:加載模板的URL

上面的helloWorld指令,使用的是template屬性,直接填充<h4>Hello world</h4>字符串作為指令的內(nèi)容。

最終,我們的helloWorld使用的,達(dá)到的效果如下:


image.png

除了,上面helloWorld使用的restrict和template外,還有一些重要的屬性,例如replace、transclude、scope、link,我們都來學(xué)習(xí)下。

replace

為true,則替換指令所在的元素;為false,則把當(dāng)前指令追加到指令所在的元素內(nèi)部,默認(rèn)false。

若是,將helloWorld指令replace設(shè)置為true,代碼如下:

demoApp.directive('helloWorld', function () {    return {        restrict: 'E',        template: '<h4>Hello world</h4>',        replace: true    }});

指令轉(zhuǎn)換結(jié)果:


image.png

transclude

為true,則把指令所在元素中原來的子節(jié)點(diǎn)移動到ng-transclude所在元素內(nèi);為false,則直接忽略內(nèi)部的子節(jié)點(diǎn),默認(rèn)false。

若是將helloWorld指令transclude設(shè)置為true,它的代碼如下:

demoApp.directive('helloWorld', function () {    return {        restrict: 'E',        template: '<h4>Hello world<span ng-transclude style="color: red"></span></h4>',        replace: true,        transclude: true    }});

指令轉(zhuǎn)換結(jié)果:

image.png

scope屬性

指令的作用域,默認(rèn)false。當(dāng)為false,為所在Dom元素上的作用域;當(dāng)設(shè)置true時(shí),創(chuàng)建一個新的作用域,所在元素的作用域是它的父作用域,它繼承父作用域上所有的屬性;當(dāng)設(shè)置scope為一個對象時(shí),創(chuàng)建一個獨(dú)立的作用域,所在元素的作用域仍然是它的parent,但它不繼承父對象上任何屬性。

當(dāng)scope設(shè)置false時(shí)

html代碼:

<body ng-controller="demoController">
    <div>指令外: {{name}}</div>
    <hello-world>測試</hello-world>
</body>

js代碼:

var demoApp = angular.module('demoApp', []);
demoApp.controller('demoController', function ($scope) {
    $scope.name = "Jack";
});

demoApp.directive('helloWorld', function () {
    return {
        restrict: 'E',
        template: '<h4>指令內(nèi)部: {{name}}</h4>',
        replace: true,
        transclude: true,
        scope: false,
        link: function (scope) {
            scope.name = "test";
        }
    }
});

效果如下:


image.png

解析:因?yàn)橹噶钔夂椭噶顑?nèi)部的是同一個作用域,所以指令內(nèi)部的name改變,也造成指令外面變化。

當(dāng)scope設(shè)置true時(shí)

html代碼:

<body ng-controller="demoController">
    <div>指令外: {{name}}</div>
    <hello-world>測試</hello-world>
</body>

js中,指令的實(shí)現(xiàn)代碼如下:

var demoApp = angular.module('demoApp', []);demoApp.controller('demoController', function ($scope) {    $scope.name = "Jack";});demoApp.directive('helloWorld', function () {    return {        restrict: 'E',        template: '<h4>指令內(nèi)部, 改變之前:{{preName}}, 改變之后: {{name}}</h4>',        replace: true,        transclude: true,        scope: true,        link: function (scope) {            scope.preName = scope.name;            scope.name = "test";        }    }});

結(jié)果:


image.png

解析:指令內(nèi)部改變之前的名字是Jack,與外面一樣;改變之后,指令外部沒有變化,仍然是test??梢苑治龀?,當(dāng)設(shè)置為true時(shí),指令內(nèi)部繼承了外部的屬性,但它是一個新的作用域。還可以通過console.log打印進(jìn)一步驗(yàn)證,外面的作用域是內(nèi)部作用域的parent。

scope設(shè)置一個對象

image.png

使用時(shí),在html中的代碼如下:

<body ng-controller="demoController">    <div>指令外: {{name}}</div>    <hello-world new-name="name">測試</hello-world></body>

js中,指令的實(shí)現(xiàn)代碼如下:

var demoApp = angular.module('demoApp', []);demoApp.controller('demoController', function ($scope) {    $scope.name = "Jack";});demoApp.directive('helloWorld', function () {    return {        restrict: 'E',        template: '<h4>指令內(nèi)部, name:{{name}}, scopeName: {{scopeName}}</h4>',        replace: true,        transclude: true,        scope: {scopeName: '=newName'},        link: function (scope) {            scope.name = scope.name;            scope.scopeName = "test";        }    }});

結(jié)果:

![image.png](https://upload-images.jianshu.io/upload_images/3120119-687abc62226d3ad4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

解析:name內(nèi)容不存在,說明沒有繼承父scope的屬性;因?yàn)槭褂谩?newName’綁定了外部屬性’name’,所以內(nèi)部改變,指令外的值也變化了。

link函數(shù)

鏈接函數(shù),在鏈接階段執(zhí)行,可以在內(nèi)部操作Dom、添加事件監(jiān)聽、設(shè)置數(shù)據(jù)綁定等等。

例如,實(shí)現(xiàn)的一個限制手機(jī)號的指令,解決了當(dāng)input為num類型時(shí)不能限制11位數(shù)字的問題。內(nèi)部使用element.on監(jiān)聽Dom事件,使用element.after添加元素。

html使用代碼:

<input type="text" ng-model="phone" placeholder="請輸入手機(jī)號" phone-limit/>

js中,指令的實(shí)現(xiàn)代碼如下:

.directive('phoneLimit', function () {    return {        restrict: "A",        scope: {            model: '=ngModel'        },        link: function (scope, element, attrs, ctrl) {            // console.log(element)            //添加一個錯誤提示元素            function appendErrorTip() {                var msg = "<span class='error-tip'></span>";                element.after(msg);                // $(element).after(msg);            }            //輸入限制            function inputLimit() {                var value = String(element.val());                //截取到哪一個字符位置                var findIndex = -1;                for(var i = 0; i < value.length; i++) {                    if(value[i] < '0' || value[i] > '9') {                        findIndex = i;                        break;                    }                    if(i >= 11) {                        findIndex = i;                        break;                    }                }                //查找到了, 則截取                if(findIndex != -1) {                    element.val(value.substr(0, findIndex));                    scope.model = element.val();                }            }            //綁定輸入事件            element.on('input', inputLimit);            //添加錯誤提示元素            appendErrorTip();            element.on("blur", function () {                var value = element.val();                //驗(yàn)證不通過,標(biāo)紅                if(value == undefined || value.length != 11) {                    element.next().html('手機(jī)號不正確');                }            })        }    }});

指令間通信

用到了兩個屬性,如下:

  • controller:創(chuàng)建一個控制器,通過這個控制器,可以實(shí)現(xiàn)指令之間的通信
  • require:要求必須存在另一個指令,當(dāng)前指令才能正確運(yùn)行

主要是通過控制器進(jìn)行通信的,通過require屬性語法,可以把require中的指令的控制器傳遞給當(dāng)前指令,從而實(shí)現(xiàn)了通信。

其中,require的用法:
如:require: '?^^myTabs'

^^前綴:說明angular會從所有父元素上查找到myTabs指令,并且獲取myTabs指令的控制器;當(dāng)是^前綴時(shí),說明angular會從當(dāng)前元素和所有父元素上查找到myTabs指令,并且獲取myTabs指令的控制器;當(dāng)沒有^^^時(shí),則說明angular只會從當(dāng)前元素查找到myTabs指令,并且獲取myTabs指令的控制器。

?前綴:可選的意思。具體是,當(dāng)沒有?前綴時(shí),angular按照上面的規(guī)則沒有查找到需要的控制器,那么就會拋出異常;如果有?前綴時(shí),則就算沒查找到,也不會拋出異常。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容