在這之前希望你對AngularJS的Directive有一定的瞭解,不然你對下面部分的理解可能會有一點難度。
步入正題:
每當一個指令被創建的時候,都會有這樣一個選擇,是繼承自己的父作用域(一般是外部的Controller提供的作用域或者根作用域($rootScope)),還是創建一個新的自己的作用域,當然AngularJS爲我們指令的
scope
參數提供了三種選擇,分別是:false
,true
,{}
;默認情況下是false
。
scope = false
首先我們來看一下,當scope
參數被設置爲false
時有什麼情況發生
在這種情況下,在指令模板中可以直接使用父作用域中的變量,函數
首先我們來創建一個指令,代碼如下所示:
JS代碼:
angular.module("MyApp", [])
.controller("MyController", function ($scope) {
//J1 這裏我們在作用域裏初始化兩個變量
$scope.name = "dreamapple";
$scope.age = 20;
//J2 創建一個方法,修改我們創建的對象的年齡
$scope.changeAge = function () {
$scope.age = 22;
}
})
//J3 創建我們的指令,指令名字爲"myDirective"
.directive("myDirective", function () {
var obj = {
//J4 指令的聲明模式爲 "AE" 屬性和元素
restrict: "AE",
//J5 指令繼承父作用域的屬性和方法
scope: false,
replace: true,
template: "<div class='my-directive'>" +
"<h3>下面部分是我們創建的指令生成的</h3>" +
"我的名字是:<span ng-bind='name'></span><br/>" +
"我的年齡是:<span ng-bind='age'></span>" +
"<input type='text' ng-model='name'>"+
" </div>"
}
return obj;
});
HTML代碼:
<div ng-app="MyApp">
<div class="container" ng-controller="MyController">
<div class="my-info">我的名字是:<span ng-bind="name"></span>
<!-- 使用"ng-bind"防止網絡狀態不佳時出現沒有被賦值表達式 -->
<br/>我的年齡是:<span ng-bind="age"></span>
</div>
<!-- 使用屬性聲明指令 -->
<div class="my-directive" my-directive></div>
</div>
</div>
CSS代碼:
div{
padding: 6px;
}
div.container {
border: 1px solid black;
}
div.my-info {
border: 1px solid blue;
}
div.my-directive{
border: 1px solid green;
}
下面我們來詳細解釋一下上面的代碼:
因爲我們將
scope
的屬性設置爲false
所以,我們創建的指令繼承了父作用域的一切屬性和方法,這也使得在指令的模板中我們可以使用這些屬性和方法。
注意:此時我們在輸入框裏改變名字,會發現上面的兩個名字都發生了變化,你肯定會說,這肯定是這樣啊,數據綁定嘛,好,我們接着往下走。
scope = true
當把scope
屬性設置爲true
時,這表明我們創建的指令要創建一個新的作用域,這個作用域繼承自我們的父作用域。
等等,剛纔我們不是說了,當把scope
屬性值設置爲false
時,不也是繼承我們的父作用域嗎?表急,我們接着往下看。
修改上面的JS代碼,將指令中的:
scope:false
修改爲scope:true
然後我們再試着在我們的input
輸入框中寫一些字符串,會發現,指令中的那個name
發生了變化,但是指令外的那個name
卻沒有發生變化,這說明了一個問題。
- 當我們將
scope
設置爲true
的時候,我們就新創建了一個作用域,只不過這個作用域是繼承了我們的父作用域;我覺得可以這樣理解,我們新創建的作用域是一個新的作用域,只不過在初始化的時候,用了父作用域的屬性和方法去填充我們這個新的作用域。它和父作用域不是同一個作用域。- 當我們將
scope
設置爲false
的時候,我們創建的指令和父作用域(其實是同一個作用域)共享同一個model
模型,所以在指令中修改模型數據,它會反映到父作用域的模型中。
Online Code Part2
scope = {}
下面我們要進入一個好玩的部分,當我們將scope
的屬性設置爲{}
時,我們可以做更多的事情。
AngularJS最強的大的地方之一就是它可以構建組建,無論放在哪裏都是可以使用的;
這所以可以做到這些,不得不歸功於指令的這個屬性;當我們將scope
設置爲{}
時,意味着我們創建的一個新的與父作用域隔離的新的作用域,這使我們在不知道外部環境的情況下,就可以正常工作,不依賴外部環境。
當然首先我們還是要給出我們的例子,先看代碼,我們修改了上述的JS代碼和HTML代碼
JS代碼:
angular.module("MyApp", [])
.controller("MyController", function ($scope) {
$scope.name = "dreamapple";
$scope.age = 20;
$scope.changeAge = function(){
$scope.age = 0;
}
})
.directive("myDirective", function () {
var obj = {
restrict: "AE",
scope: {
name: '@myName',
age: '=',
changeAge: '&changeMyAge'
},
replace: true,
template: "<div class='my-directive'>" +
"<h3>下面部分是我們創建的指令生成的</h3>" +
"我的名字是:<span ng-bind='name'></span><br/>" +
"我的年齡是:<span ng-bind='age'></span><br/>" +
"在這裏修改名字:<input type='text' ng-model='name'><br/>" +
"<button ng-click='changeAge()'>修改年齡</button>" +
" </div>"
}
return obj;
});
HTML代碼:
<div ng-app="MyApp">
<div class="container" ng-controller="MyController">
<div class="my-info">我的名字是:<span ng-bind="name"></span>
<br/>我的年齡是:<span ng-bind="age"></span>
<br />
</div>
<div class="my-directive" my-directive my-name="{{name}}" age="age" change-my-age="changeAge()"></div>
</div>
</div>
我們使用了隔離的作用域,不代表我們不可以使用父作用域的屬性和方法。
- 我們可以通過向
scope
的{}
中傳入特殊的前綴標識符(即prefix
),來進行數據的綁定。 - 在創建了隔離的作用域,我們可以通過
@
,&
,=
引用應用指令的元素的屬性,如上面的代碼那樣,我們可以在<div class="my-directive" my-directive my-name="{{name}}" age="age" change-my-age="changeAge()"></div>
這個元素中,利用前綴標識符通過使用屬性my-name
,age
,change-my-age
來引用這些屬性的值。
下面我們來看看如何使用這些前綴標識符:
@
這是一個單項綁定的前綴標識符
使用方法:在元素中使用屬性,好比這樣<div my-directive my-name="{{name}}"></div>
,注意,屬性的名字要用-
將兩個單詞連接,因爲是數據的單項綁定所以要通過使用{{}}
來綁定數據。
=
這是一個雙向數據綁定前綴標識符
使用方法:在元素中使用屬性,好比這樣<div my-directive age="age"></div>
,注意,數據的雙向綁定要通過=
前綴標識符實現,所以不可以使用{{}}
。
&
這是一個綁定函數方法的前綴標識符
使用方法:在元素中使用屬性,好比這樣<div my-directive change-my-age="changeAge()"></div>
,注意,屬性的名字要用-
將多個個單詞連接。
注意:在新創建指令的作用域對象中,使用屬性的名字進行綁定時,要使用駝峯命名標準,比如下面的代碼。
scope: {
// `myName` 就是原來元素中的`my-name`屬性
name: '@myName',
age: '=',
// `changeMyAge`就是原來元素中的`change-my-age`屬性
changeAge: '&changeMyAge'
}
進一步說明,我們的指令是如何利用這些前綴標識符來尋找我們想要的屬性或者函數的?
@
當指令編譯到模板的name
時,就會到scope
中尋找是否含有name
的鍵值對,如果存在,就像上面那樣,看到@
就知道這是一個單向的數據綁定,然後尋找原來的那個使用指令的元素上(或者是指令元素本身)含有這個值的屬性即my-name={{name}}
,然後在父作用域查找{{name}}
的值,得到之後傳遞給模板中的name
。=
和&
與@
差不多,只不過=
進行的是雙向的數據綁定,不論模板還是父作用域上的屬性的值發生改變都會使另一個值發生改變,而&
是綁定函數而已。