從零學前端第十三講:AngularJs進階-指令

修真院Web工程師零基礎全能課javascript

 

本節課內容html

AngularJs進階-指令java

 

主講人介紹angularjs

沁修,葡萄藤技術總監app

項目經驗豐富,擅長H5移動項目開發。異步

專一技術選型、底層開發、最佳代碼實踐規範總結與推廣。函數

 

直播錄屏版post

傳送門:https://v.qq.com/x/page/p0775...性能

 

 

文字版解析ui

 

概述

 

指令是什麼?

 

其實咱們從一開始接觸到angularjs就在使用指令了

指令就像是教給html的一些小花招,它會附加在html的元素上,以自定義標記的方式,告訴angularjs的html編譯器:

這某個元素加上了一些特定的行爲或者是方法,還能夠對html的元素進行各類操做。

 

angularjs中它自己是內置了一整套指令的,好比ngBind,ngClick,ngApp,ngView等等

但也能夠本身建立本身的指令來方便咱們的開發和使用。

 

 

 

 

咱們先來看一下怎麼去使用指令directive?

當angular啓動咱們的程序時,html編譯器$compile就會遍歷整個DOM,而且會去匹配DOM元素裏的指令

 

那這些指令一般是怎麼放置在html中的呢?

一般有4種方法:

 

<!-- 1. 屬性的方式 -->

<spanmy-dir="exp"></span>

<!-- 2. 類的方式 -->

<spanclass="my-dir: exp;"></span>

<!-- 3. 元素的方式 -->

<my-dir></my-dir>

<!-- 4. 註釋的方式 -->

<!-- directive: my-dir exp -->

 

不過建議你們以屬性的方式爲主,元素的方式爲輔,其餘的兩種方式能夠不去使用。

這樣更容易看出來一個元素是跟那個指令相匹配的,更由於不少的指令都被限制爲屬性的方式。

這裏就很少作演示了,咱們默認都使用屬性的方式。

 

 

 

 

指令的命名規範是?

 

指令有許多,如ngView, ngBind, ngModel等等。

但寫在html中的時候有個問題就是html是不識別大小寫區別的,所以駝峯的寫法就會有問題。

 

有了指令匹配到元素上後,咱們的編譯器還須要經過另外的命名規範來匹配它們是不是一個指令:

所以命名規範會將駝峯式改成全小寫,而後用破折號或者其餘符號間隔的形式來實現,好比如下寫法都是正確合法的:

 

  • ng:bind
  • ng-bind
  • ng_bind
  • x-ng-bind
  • data-ng-bind

 

咱們試着使用一下它:

<divng-controller="Fn1">

<p>{{ user }}</p>

</div>

<divng_controller="Fn2">

<p>{{ user }}</p>

</div>

<divng:controller="Fn3">

<p>{{ user }}</p>

</div>

<divdata-ng-controller="Fn4">

<p>{{ user }}</p>

</div>

<divx-ng-controller="Fn5">

<p>{{ user }}</p>

</div>

<scriptsrc="bower_components/angular/angular.js"></script>

<scripttype="text/javascript">

varmyApp=angular.module("myApp", []);

myApp.controller('Fn1',function($scope) {

$scope.user='Alex';

});

myApp.controller('Fn2',function($scope) {

$scope.user='Bill';

});

myApp.controller('Fn3',function($scope) {

$scope.user='Coco';

});

myApp.controller('Fn4',function($scope) {

$scope.user='Danel';

});

myApp.controller('Fn5',function($scope) {

$scope.user='Eleven';

});

 

 

接下來看看指令在html中的編譯步驟:

 

 

1.首先,將html轉換爲DOM對象

 

2.而後它會遍歷整個DOM,識別指令,將指令和與之對應DOM一塊兒放到directive列表當中。

按順序執行他們每個的compile()函數(編譯函數),這個時候他們會擁有一個修改DOM結構的機會,而且會產生一個link()函數。

所以最後會返回一個組合的linking函數,是頁面中全部指令自身的compile函數返回的linking函數集合。

 

3.經過上一步返回的linking函數集合,把html模板和scope做用域鏈接起來,同時還能註冊一些監聽器。

最後讓scope和DOM之間造成雙向即時的綁定。

 

咱們來看一個例子:

 

<inputtype="text"ng-model="userName"><buttonng-click="addUser()">add</button>

<ul>

<ling-repeat="user in users">{{user}}</li>

</ul>

 

經過這個例子,咱們來理解編譯過程爲啥要分爲compile和link兩步。

就是由於有時候須要在model發生改變後,對應DOM結構也要跟着改變。

 

其中ng-model就是一個指令,ng-repeat又是另一個。

但ng-repeat有一個問題就是它須要很快的根據model製造出多個新的li,li元素須要被克隆和插入到ul當中。

 

並且僅僅只是克隆是不夠的,還需的要編譯,以便它的{{user}}能夠被正確的scope解析。

若是咱們只是簡單的拷貝插入一個li元素,而後編譯,這樣每個li元素髮生改變,都會遍歷整個DOM樹從新運行。

若是的repeat的item有成百上千個,就會有性能問題。

 

因此解決方案就是把編譯分爲兩個步驟:

compile階段識別全部的directive,而後按照優先級排序,該改變DOM就改變DOM。

到linking階段再將scope和li綁定在一塊兒。

 

 

 

 

自定義指令?

 

咱們先寫一個最簡單的指令:

好比有這麼一段帶有我的信息的模板,會在代碼中重複屢次,那麼咱們就可使用指令來簡化這個過程。

固然指令和控制器同樣,也是註冊在模塊上的,咱們先建立一個指令,來替換本來的內容:

 

<divng-controller="DirCtrl1">

<divppt-time></div>

</div>

varmyApp=angular.module("myApp", []);

myApp.controller('DirCtrl',function($scope) {

$scope.user='Tom';

$scope.current=newDate();

$scope.task=2;

})

myApp.directive('pptTime',function() {

return{

template:'{{user}}領取任務{{task}}的時間點爲:{{current}}'

}

})

 

這裏能夠看到,控制器DirCtrl裏的變量,能夠被指令使用並展現在視圖上。

咱們來看看發生了什麼事:

 

1.首先將HTML轉換爲DOM對象。

 

2.對DOM對象進行編譯,遍歷DOM而且對指令進行匹配。

匹配上的指令會有個修改DOM結構的機會,而且產生一個link函數並返回link函數集合

 

3.經過返回的link函數集合,把模板和scope鏈接起來。

這樣在scope和DOM之間會有一個雙向即時的綁定,當scope發生變化的時候,DOM也會發生對應的改變。

 

這樣咱們就有基本的指令可使用了,但這樣有一點很差就是這不是一個好的代碼實踐:

咱們但願將模板分割出來,讓視圖和代碼分離,所以能夠將template這個選項改成templateUrl來加載相應的html模板文件,會優雅許多。

 

 

自定義指令的模板

 

angular.module('app', []).directive('myDirective',function() {

return{

restrict:String,

priority:Number,

terminal:Boolean,

template:String,

templateUrl:String,

replace:BooleanorString,

transclude:Boolean,

scope:BooleanorObject,

controller:Stringorfunction(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) { }

}

returnfunctionpostLink() { }

}

}

})

 

 

咱們來看一下這個自定義的指令的參數,首先restrict的值是一個string,做用是申明在模板中如何使用,它有四個值:

 

E,A,C,M

 

一般咱們都推薦使用元素和屬性的方式

 

E元素<ptt-time></ptt-time>A屬性(默認)<div ptt-time></div>C樣式類<div class=「ptt-time」></div>M註釋<!— directive: ptt-time -->

 

 

priority: number表示的是指令的執行優先級,若是在單個DOM上有多個指令,則優先執行優先級高的。這個指令不多使用

 

template,是指令連接的DOM模板

 

templeteUrl,是指定一個字符串形式的內嵌模板,加載的模板是經過異步加載的

 

replace,是指令連接模板是否替換原有的元素

 

<hello>

<div>這裏是指令內部的內容。</div>

</hello>

angular.module("MyModule", []).myModule.directive("hello",function() {

return{

restrict:"AE",

template:"<div>Hello World!</div>",

replace:true

}

});

 

transclude,和replace有一點類似,但它是讓標識符裏原有的內容帶到新的位置上。

開啓這個以後,就能夠在指令的模板中使用ng-transclude來指明什麼地方放內容。

 

<hello>

<div>這裏是指令內部的內容。</div>

</hello>

angular.module("MyModule", []).myModule.directive("hello",function() {

return{

restrict:"AE",

transclude:true,

template:"<div>Hello everyone!<div ng-transclude>你看不見我</div></div>"

}

});

 

link,這是爲目標DOM元素添加事件監聽,創建數據綁定。

它的scope參數是表示與指令元素相關的做用域,element是當前指令所對應的元素,attrs是當前元素的屬性所組成的對象

 

controller,是船艦一個控制器,經過標識符來公開通訊API,而且給指令暴露出一組方法,供外部調用。

它的參數scope是和指令相關聯的做用域:

element,是當前指令對應的元素

attrs,是當前元素的屬性組成的對象

transclude,是嵌入連接函數

 

其實指令的controller和link函數能夠互換,它們的區別在於:

控制器主要提供能夠在指令之間複用的行爲,但link是隻能在當前內部指令中執行的行爲且沒法在指令之間複用。

 

關於controller和require的用法範例

require,它的值表明了另外個指令的名字,而且會做爲link的第四個參數。

 

 

假設如今咱們要編寫三個指令,三個指令中的link連接函數中存在有不少重合的方法。

這時候咱們就能夠將這些重複的方法寫在一個指令的controller中。

 

 

而後在這三個指令中:

require這個擁有controller字段的的指令,最後經過link連接函數的第四個參數就能夠引用這些重合的方法了。

 

<superman>super</superman>

<superman speed>super and speed </superman>

 

varmyModule=angular.module("MyModule", []);

myModule.directive("superman",function() {

return{

scope: {},

restrict:'AE',

controller:function($scope) { // 定義了三個公共方法,供外部指令訪問

$scope.abilities=[];

this.addStrength=function() {

$scope.abilities.push("strength");

};

this.addSpeed=function() {

scope.abilities.push("speed");

};

this.addLight=function() {

$scope.abilities.push("light");

};

},

link:function(scope, element, attrs) {

element.bind("mouseenter",function() {

console.log(scope.abilities);

});

}

}

});

 

myModule.directive("speed",function() {

return{

require:'^superman',

link:function(scope, element, attrs, supermanCtrl) {

supermanCtrl.addSpeed();

}

}

});

 

最後講一下scope,它的做用是建立一個新的做用域。

它的值有3種:

false,true,{}

 

當它等於false的時候,咱們在哪一個controller中使用這個指令,它就會繼承這個ctrl的值:

也就是說它們之間不會隔離,哪一邊的值發生變化,另一邊也會跟着變:

 

myApp.directive('hello',function() {

return{

restrict:'AE',

scope:false,

template:'<div><input type="text" ng-model="userName"/> {{ userName }} </div>',

replace:true

}

})

 

 

當它等於true的時候,指令就會繼承控制器的值,改變控制器裏的值,指令裏面的值也會隨之變化。

可是改變指令的值,控制器的卻不會變,這就是產生了繼承隔離。

 

當它等於{}時,就不會繼承任何值,徹底產生隔離。

而後,給你們佈置一個做業,關於scope裏的屬性綁定策略,有:

@attr單向文本綁定

=attr雙向綁定

&調用符做用域中的函數

 

本身去了解它們是怎麼使用的。

 

以上就是上節課的內容解析啦

相關文章
相關標籤/搜索