指令(directive)是angular里面最核心也是最難懂的東西,在慕課網看了下大漠窮秋老濕的視頻,自己百度半天做了一些小test,總算把一切都搞明白了。
先列出學習來源:
指令中controller和link的區別:http://www.cnblogs.com/CreateMyself/p/5568202.html
angular視頻教程:http://www.imooc.com/learn/156
指令中的隔離 Scope :https://blog.coding.net/blog/angularjs-directive-isolate-scope
angular學習筆記:https://www.zouyesheng.com/angular.html#toc68
一、指令的創建:
首先你得先創建一個module:
var module1 = angular.module('module1',[]);
angular.bootstrap(document.body,['module1']);
然后你還得有一個對應的controller:
var module1 = angular.module('module1',[]); module1.controller('ctl1', function($scope) { $scope.content = 'i\'m, module 1'; $scope.name = 'module1'; $scope.save = function() { console.log('is function save'); }; });
angular.bootstrap(document.body,['module1']);
然后你就可以安心的創建指令了:
// 銜接上面的代碼
m1.directive('testDirective', function() { // 將對象return出去 return{ restrict: 'E',// 指令類型 E:element A:attribute M:comment C: class template: '<div>我是指令生成的內容</div>'; replace: true, //使用模板替換原始標記 指令內原本的數據將被清空 } });
angular.bootstrap(document.body,['module1']);
對應的html可以這樣寫:
<body> <div ng-controller="ctl1">{{content}} <test-directive>這是原本的內容</test-directive> </div> </body>
以上代碼需要注意一下幾點:
1.我們定義的指令名稱是testDirective,但是在html中要寫成test-directive。
2.我們把指令里面的代碼都放在了function中的return里面,其實return出去的內容就是整個指令對象。
3.angular.bootstrap(document.body,['module1']);相當於我們在html中使用ng-app指令。推薦使用bootstarp而不是ng-app;
二、指令的屬性
指令的屬性如下:
- name
- priority
- terminal
- scope
- controller
- require
- restrict
- template
- templateUrl
- replace
- transclude
- compile
- link
其中的name、priority、terminal不做詳細的介紹。
name就是指令名,對應上面代碼中的testDirective,priority多個指令設置在同一個元素上的執行優先級,執行順序從低至高:1>2>3.priority的值為正整數,比如priority: 1,
terminal, true/false 如果為true,同一個元素上的其他指令的優先級高於本指令,其他指令將停止執行
1.restrict屬性
angular內置的一些指令,比如ng-app,ng-click之類的,都是以html元素的屬性(atrrbile)的形式來實現的,我在使用ionic框架的時候,里面有很多自定義的標簽、比如:<ion-content>
</ion-content>。這也是一個指令,他是通過html元素(element)來實現的。除了這兩個之外,指令還支持class(html標簽的class屬性)、commment(html中的注釋)來實現。
在JS代碼中,restrict可以有以下賦值:
restrict: 'AE',// 指令類型 E:element A:attribute M:comment C: class
可以是多個restrict: 'AEMC',也可以是一個restrict: 'E'。在html中對應的寫法為:
其中注釋: <!-- 兩邊一定要留空格,不然什么都不會發生 -->
2.template和templateUrl
同一個指令中只能template和templateUrl只能選其一。
template為模板內容。即你要在指令所在的容器中插入的html代碼。
template屬性接收一個字符串,類似這樣:
template: '<div>我是指令生成的內容</div>';
你也可以將整個template寫得很復雜,但是,復雜的代碼非常不易維護。並且你還得換行,得用字符串拼接每一行。
當你的tamplate超出10行就會顯得辣眼睛,過長的template建議使用templateUrl代替。
templateUrl為從指定地址獲取模板內容。即你要在指令所在的容器中插入的一個.html文件。
有了templateUrl我們就可以將想實現的內容寫成一個單獨的html模版,在需要的地方插入,使用起來會很舒服。
這里的templateUrl類似於JSP中的include,angular也有一個ng-include指令。
具體的詳細請點連接:http://www.ziqiangxuetang.com/angularjs/angularjs-include.html
3.replace和transclude
replace:是否用模板替換當前元素。true : 將指令標簽替換成temple中定義的內容,頁面上不會再有<my-directive>
標簽;false :則append(追加)在當前元素上,即模板的內容包在<my-directive>
標簽內部。默認false。
transculde:是否使用ng-transculde來包含html中指令包含的原有的內容,接收兩個參數true/false
先上例子:replace
var app = angular.module("app", []) .directive("hello", function () { var option = { restrict: "AECM", template: "<h3>Hello, Directive</h3>", replace: true //這里replace為true,所以原來的內容會被template代替 }; return option; })
<html> <head></head> <body> <hello>我是原來的內容</hello> ===> 變成<h3>Hello, Directive</h3>
如果replace為false ===><hello><h3>Hello, Directive</h3></hello>
</body>
</html>
transclude:
var app = angular.module("app", []) .directive("hello", function () { var option = { restrict: "AECM", template: "<h3>Hello, Directive</h3><span ng-transclude></span>", transculde: true //這里transculde為true,所以原來的內容會被放在有ng-transclude屬性的標簽內 }; return option; })
<html> <head></head> <body> <hello>我是原來的內容</hello> ===> 變成<hello><h3>Hello, Directive</h3><span ng-transclude>我是原來的內容</span></hello> </body> </html>
4.指令中的scope
directive 默認能共享父 scope 中定義的屬性,例如在模版中直接使用父 scope 中的對象和屬性。通常使用這種直接共享的方式可以實現一些簡單的 directive 功能。
但是,當你要創建一個可以重復使用的directive的時候,就不能依賴於父scope了,因為在不同的地方使用directive對應的父scope不一樣。
所以你需要一個隔離的scope,我們可以向下面這樣來定義我們的scope。
module1.directive("testDirective", function () {
return {
scope: {
value: '提莫隊長正在待命!'
},
template: 'Say:{{value}}'
}
});
這樣就很方便的將我們directive的上下文scope給定義出來了,但是,如果我想將父scope中的屬性傳遞給directive的scope怎么辦呢?
directive 在使用隔離 scope 的時候,提供了三種方法同隔離之外的地方交互:
- @ 綁定一個局部 scope 屬性到當前 dom 節點的屬性值。結果總是一個字符串,因為 dom 屬性是字符串。
- & 提供一種方式執行一個表達式在父 scope 的上下文中。如果沒有指定 attr 名稱,則屬性名稱為相同的本地名稱。
- = 通過 directive 的 attr 屬性的值在局部 scope 的屬性和父 scope 屬性名之間建立雙向綁定。
以上三種方式都要在directive的attr屬性中進行賦值。上面的話理解起來比較困難,我根據自己的理解做了一下修改:
@:只能綁定字符串,所以一些簡單的繼承父scope的屬性使用@
=: 需要實現雙向數據綁定的時候使用=
&: 提供一種方式執行一個表達式在父scope的上下文中,即使用於將父scope中的函數綁定在指令的scope中
以上的理解也許有些偏頗,歡迎指正。
(1)先說@
app.controller("ctl1", function ($scope) { $scope.name = "hello world"; }).directive("testDirective", function () { return { scope: { name: "@" }, template: 'Say:{{name}} <br>改變隔離scope的name:<input type="buttom" value="" ng-model="name" class="ng-pristine ng-valid">' } })
<div ng-controller="ctl1"> <div> <div>父scope: <div>Say:{{name}}<br>改變父scope的name:<input type="text" value="" ng-model="name"/></div> </div> <div>隔離scope:這個顯示為hello world <div test-directive name="{{name}}"></div> </div> <div>隔離scope(不使用{{name}}這個就直接顯示為name了): <div test-directive name="name"></div> </div> </div>
我們在test-directive指令所在的div上面,增加了一個name屬性,要使用雙花括號來給屬性賦值。也可以寫成nameCopy:'@nameForCtl',這樣寫,在給directive中的scope的屬性賦值的時候,獲取查詢@后面的name這個標識對應的屬性的值(這里nameForCtl在js中是駝峰寫法,同樣的在html中對應的屬性名應該寫成name-for-ctl)。不是很推薦這種寫法,感覺有點多余。
(2)=
上一個例子中,我們使用name="{{name}}"的形式來傳遞父scope 的屬性對應的值,so,我們只是把對應的值傳遞給了directive的scope,當我想實現在directive中改變父scope傳遞過來的值時,父scope中的值也對應的改變,顯然用@這種方法走不通。
這時=就派上用場了。
app.controller("ctl1", function ($scope) { $scope.user = { name: 'hello', id: 1 }; }).directive("testDirective", function () { return { scope: { user: "=" }, template: 'Say:{{user.name}} <br>改變隔離scope的name:<input type="buttom" value="" ng-model="user.name"/>' } })
<div ng-controller="ctl1"> <div>父scope: <div>Say:{{user.name}}<br>改變父scope的name:<input type="text" value="" ng-model="user.name"/></div> </div> <div>隔離scope: <div isolated-directive user="user"></div> </div> <div>隔離scope(使用{{name}},這個會報錯): <div isolated-directive user="{{user}}"></div> </div> </div>
這一個例子和上一個例子不同的地方就是屬性賦值的時候,一個應該使用{{}},一個不該使用。=為了實現雙向數據綁定,angular會使用‘=’對應的屬性的值與父scope中的屬性進行匹配,然后傳遞給diractive中的scope。至於實現的細節和原理,這里我就不說了(其實是不大清楚)。
(3)&
& 方式提供一種途經使directive 能在父 scope 的上下文中執行一個表達式。此表達式可以是一個 function。其實說白了,就是可以使用在父scope中定義的函數。
比如:當你寫了一個 directive,當用戶點擊按鈕時,directive 想要通知 controller,controller 無法知道 directive 中發生了什么,也許你可以通過使用 angular 中的 event 廣播來做到,但是必須要在 controller 中增加一個事件監聽方法。
最好的方法就是讓 directive 可以通過一個父 scope 中的 function,當 directive 中有什么動作需要更新到父 scope 中的時候,可以在父 scope 上下文中執行一段代碼或者一個函數。
app.controller("ctl1", function ($scope) { $scope.value = "hello world"; $scope.click = function () { $scope.value = Math.random(); }; }).directive("testDirective", function () { return { scope: { action: "&" }, template: '<input type="button" value="在directive中執行父scope定義的方法" ng-click="action()"/>' } })
<div ng-controller="ctl1"> <div>父scope: <div>Say:{{value}}</div> </div> <div>隔離scope: <div isolated-directive action="click()"></div> </div> </div>
在上面的例子中,我們的屬性action賦值為一個方法:action="click()",這樣一寫,一眼就看出來是個什么東西了,好像也沒什么好解釋的。
5.controller、require以及link
這三個涉及到指令的執行過程,本人暫時還沒有比較完全的理解,所以這里只能讓大家去看別人寫的博客了(原諒我太笨);
連接在頂部。嗯。 ↑點擊返回頂部