一招制敵 - 玩轉 AngularJS 指令的 Scope (做用域)

學習了AngularJS好長時間,最近再次回首看看指令這部分的時候,以爲比本身剛開始學習的時候理解的更加深刻了,尤爲是指令的做用域這部分。javascript

當初看的是《AngularJS權威指南》這本書,可是感受這本書關於這方面講的不是很細緻,
另外吐槽一下,這本書中文版印刷的質量不是很好,不少地方都有錯誤;不過講的仍是能夠的,是一本學習AngularJS的好書。css

下面咱們就來詳細分析一下指令的做用域。
在這以前但願你對AngularJS的Directive有必定的瞭解,否則你對下面部分的理解可能會有一點難度。html

步入正題:java

每當一個指令被建立的時候,都會有這樣一個選擇,是繼承本身的父做用域(通常是外部的Controller提供的做用域或者根做用域($rootScope)),仍是建立一個新的本身的做用域,固然AngularJS爲咱們指令的scope參數提供了三種選擇,分別是:false,true,{};默認狀況下是false網絡

scope = false

首先咱們來看一下,當scope參數被設置爲false時有什麼狀況發生app

在這種狀況下,在指令模板中能夠直接使用父做用域中的變量,函數
首先咱們來建立一個指令,代碼以下所示:
JS代碼:函數

javascriptangular.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代碼:學習

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代碼:spa

cssdiv{
    padding: 6px;
}
div.container {
    border: 1px solid black;
}
div.my-info {
    border: 1px solid blue;
}
div.my-directive{
    border: 1px solid green;
}

Online Code Part1.net

下面咱們來詳細解釋一下上面的代碼:

由於咱們將scope的屬性設置爲false因此,咱們建立的指令繼承了父做用域的一切屬性和方法,這也使得在指令的模板中咱們可使用這些屬性和方法。

注意:此時咱們在輸入框裏改變名字,會發現上面的兩個名字都發生了變化,你確定會說,這確定是這樣啊,數據綁定嘛,好,咱們接着往下走。

scope = true

當把scope屬性設置爲true時,這代表咱們建立的指令要建立一個新的做用域,這個做用域繼承自咱們的父做用域。
等等,剛纔咱們不是說了,當把scope屬性值設置爲false時,不也是繼承咱們的父做用域嗎?表急,咱們接着往下看。

修改上面的JS代碼,將指令中的:

scope:false修改成scope:true

而後咱們再試着在咱們的input輸入框中寫一些字符串,會發現,指令中的那個name發生了變化,可是指令外的那個name卻沒有發生變化,這說明了一個問題。

  1. 當咱們將scope設置爲true的時候,咱們就新建立了一個做用域,只不過這個做用域是繼承了咱們的父做用域;我以爲能夠這樣理解,咱們新建立的做用域是一個新的做用域,只不過在初始化的時候,用了父做用域的屬性和方法去填充咱們這個新的做用域。它和父做用域不是同一個做用域。
  2. 當咱們將scope設置爲false的時候,咱們建立的指令和父做用域(實際上是同一個做用域)共享同一個model模型,因此在指令中修改模型數據,它會反映到父做用域的模型中。
    Online Code Part2

scope = {}

下面咱們要進入一個好玩的部分,當咱們將scope的屬性設置爲{}時,咱們能夠作更多的事情。

AngularJS最強的大的地方之一就是它能夠構建組建,不管放在哪裏都是可使用的;
這因此能夠作到這些,不得不歸功於指令的這個屬性;當咱們將scope設置爲{}時,意味着咱們建立的一個新的與父做用域隔離的新的做用域,這使咱們在不知道外部環境的狀況下,就能夠正常工做,不依賴外部環境。

固然首先咱們仍是要給出咱們的例子,先看代碼,咱們修改了上述的JS代碼和HTML代碼
JS代碼:

javascriptangular.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代碼:

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>

Online Code Part3

咱們使用了隔離的做用域,不表明咱們不可使用父做用域的屬性和方法。

  1. 咱們能夠經過向scope{}中傳入特殊的前綴標識符(即prefix),來進行數據的綁定。
  2. 在建立了隔離的做用域,咱們能夠經過@,&,=引用應用指令的元素的屬性,如上面的代碼那樣,咱們能夠在<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>,注意,屬性的名字要用-將多個個單詞鏈接。

注意:在新建立指令的做用域對象中,使用屬性的名字進行綁定時,要使用駝峯命名標準,好比下面的代碼。

javascriptscope: {
            // `myName` 就是原來元素中的`my-name`屬性
            name: '@myName', 
            age: '=',
            // `changeMyAge`就是原來元素中的`change-my-age`屬性
            changeAge: '&changeMyAge' 
        }

進一步說明,咱們的指令是如何利用這些前綴標識符來尋找咱們想要的屬性或者函數的?

  • @ 當指令編譯到模板的name時,就會到scope中尋找是否含有name的鍵值對,若是存在,就像上面那樣,看到@就知道這是一個單向的數據綁定,而後尋找原來的那個使用指令的元素上(或者是指令元素自己)含有這個值的屬性即my-name={{name}},而後在父做用域查找{{name}}的值,獲得以後傳遞給模板中的name
  • =&@差很少,只不過=進行的是雙向的數據綁定,不論模板仍是父做用域上的屬性的值發生改變都會使另外一個值發生改變,而&是綁定函數而已。

到這裏咱們關於AngularJS指令的做用域估計也理解的差很少了,那好,洗洗睡吧!

相關文章
相關標籤/搜索