AngularJs之Scope作用域

来源:http://www.cnblogs.com/puyongsong/archive/2016/10/31/6016932.html
-Advertisement-
Play Games

前言: 上篇博文AngularJs之directive中說了Scope作用域是個大坑,所以拿出來作為重點總結! 什麼是scope AngularJS 中,作用域是一個指嚮應用模型的對象,它是表達式的執行環境。作用域有層次結構,這個層次和相應的 DOM 幾乎是一樣的。作用域能監控表達式和傳遞事件。 在 ...


前言:

上篇博文AngularJs之directive中說了Scope作用域是個大坑,所以拿出來作為重點總結!

什麼是scope

  AngularJS 中,作用域是一個指嚮應用模型的對象,它是表達式的執行環境。作用域有層次結構,這個層次和相應的 DOM 幾乎是一樣的。作用域能監控表達式和傳遞事件。

  在 HTML 代碼中,一旦一個 ng-app 指令被定義,那麼一個作用域就產生了,由 ng-app 所生成的作用域比較特殊,它是一個根作用域($rootScope),它是其他所有$Scope 的最頂層。

  除了用 ng-app 指令可以產生一個作用域之外,其他的指令如 ng-controller,ng-repeat 等都會產生一個或者多個作用域。此外,還可以通過 AngularJS 提供的創建作用域的工廠方法來創建一個作用域。這些作用域都擁有自己的繼承上下文,並且根作用域都為$rootScope。

  在生成一個作用域之後,在編寫 AngularJS 代碼時,$scope 對象就代表了這個作用域的數據實體,我們可以在$scope 內定義各種數據類型,之後可以直接在 HTML 中以 {{變數名}} 方式來讓 HTML 訪問到這個變數。

繼承作用域

  AngularJS 在創建一個作用域時,會檢索上下文,如果上下文中已經存在一個作用域,那麼這個新創建的作用域就會以 JavaScript 原型繼承機制繼承其父作用域的屬性和方法。

  一些 AngularJS 指令會創建新的子作用域,並且進行原型繼承: ng-repeat、ng-include、ng-switch、ng-view、ng-controller, 用 scope: true 和 transclude: true 創建的 directive。

  以下 HTML 中定義了三個作用域,分別是由 ng-app 指令所創建的$rootScope,parentCtrl 和 childCtrl 所創建的子作用域,這其中 childCtrl 生成的作用域又是 parentCtrl 的子作用域。

示例一:作用域的繼承實例

<!doctype html>
<html>
<head>
    <meta charset=utf-8"/>
    <title>scope nick</title>
    <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<script type="text/javascript">
    angular.module('app', [])
            .controller('parentCtrl', ['$scope', function($scope) {
                $scope.args= 'Nick DeveloperWorks';
            }])
            .controller('childCtrl', ['$scope', function($scope) {
                $scope.args= 'Nick DeveloperWorks for test';
            }]);
</script>
<body ng-app="app">
<div ng-controller="parentCtrl">
    <input ng-model="args">
    <div ng-controller="childCtrl">
        <input ng-model="args">
    </div>
</div>
</body>
</html>

  繼承作用域符合 JavaScript 的原型繼承機制,這意味著如果我們在子作用域中訪問一個父作用域中定義的屬性,JavaScript 首先在子作用域中尋找該屬性,沒找到再從原型鏈上的父作用域中尋找,如果還沒找到會再往上一級原型鏈的父作用域尋找。在 AngularJS 中,作用域原型鏈的頂端是$rootScope,AnguarJS 將會尋找到$rootScope 為止,如果還是找不到,則會返回 undefined。

  我們用實例代碼說明下這個機制。首先,我們探討下對於原型數據類型的作用域繼承機制:

示例二:作用域繼承實例-原始類型數據繼承

<!doctype html>
<html>
<head>
    <meta charset=utf-8"/>
    <title>scope nick</title>
    <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<script type="text/javascript">
    angular.module('app', [])
            .controller('parentCtrl', ['$scope', function($scope) {
                $scope.args = 'Nick DeveloperWorks';
            }])
            .controller('childCtrl', ['$scope', function($scope) {
            }]);
</script>
<body ng-app="app">
<div ng-controller="parentCtrl">
    <input ng-model="args">
    <div ng-controller="childCtrl">
        <input ng-model="args">
    </div>
</div>
</body>
</html>

測試運行結果:

第一個輸入框:

  雖然在 childCtrl 中沒有定義具體的 args 屬性,但是因為 childCtrl 的作用域繼承自 parentCtrl 的作用域,因此,AngularJS 會找到父作用域中的 args 屬性並設置到輸入框中。而且,如果我們在第一個輸入框中改變內容,內容將會同步的反應到第二個輸入框。

第二個輸入框:

  第二個輸入框的內容從此將不再和第一個輸入框的內容保持同步。在改變第二個輸入框的內容時,因為 HTML 代碼中 model 明確綁定在 childCtrl 的作用域中,因此 AngularJS 會為 childCtrl 生成一個 args 原始類型屬性。這樣,根據 AngularJS 作用域繼承原型機制,childCtrl 在自己的作用域找得到 args 這個屬性,從而也不再會去尋找 parentCtrl 的 args 屬性。從此,兩個輸入框的內容所綁定的屬性已經是兩份不同的實例,因此不會再保持同步。

現將代碼做如下修改,結合以上兩個場景,會出現怎樣的結果?

示例三:作用域繼承實例-對象數據繼承

<!doctype html>
<html>
<head>
    <meta charset=utf-8"/>
    <title>scope nick</title>
    <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<script type="text/javascript">
    angular.module('app', [])
            .controller('parentCtrl', ['$scope', function($scope) {
                $scope.args = {};
                $scope.args.content = 'Nick DeveloperWorks';
            }])
            .controller('childCtrl', ['$scope', function($scope) {
            }]);
</script>
<body ng-app="app">
<div ng-controller="parentCtrl">
    <input ng-model="args.content">
    <div ng-controller="childCtrl">
        <input ng-model="args.content">
    </div>
</div>
</body>
</html>

測試結果是無論改變任何一個輸入框的內容,兩者的內容始終同步。

  根據 AngularJS 的原型繼承機制,如果 ng-model 綁定的是一個對象數據,那麼 AngularJS 將不會為 childCtrl 創建一個 args 的對象,自然也不會有 args.content 屬性。這樣,childCtrl 作用域中將始終不會存在 args.content 屬性,只能從父作用域中尋找,也即是兩個輸入框的的變化其實只是在改變 parentCtrl 作用域中的 args.content 屬性。因此,兩者的內容始終保持同步。

  我們再看一個例子,分析結果如何。

示例四:作用域繼承實例-不再訪問父作用域的數據對象。

<!doctype html>
<html>
<head>
    <meta charset=utf-8"/>
    <title>scope nick</title>
    <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<script type="text/javascript">
    angular.module('app', [])
            .controller('parentCtrl', ['$scope', function($scope) {
                $scope.args = {};
                $scope.args.content = 'Nick DeveloperWorks';
            }])
            .controller('childCtrl', ['$scope', function($scope) {
                $scope.args = {};
                $scope.args.content = 'Nick DeveloperWorks for test';
            }]);
</script>
<body ng-app="app">
<div ng-controller="parentCtrl">
    <input ng-model="args.content">
    <div ng-controller="childCtrl">
        <input ng-model="args.content">
    </div>
</div>
</body>
</html>

  測試結果是兩個輸入框的內容永遠不會同步。子作用域有實例數據對象,則不訪問父作用域。

獨立作用域

  獨立作用域是 AngularJS 中一個非常特殊的作用域,它只在 directive 中出現。在對 directive 的定義中,我們添加上一個 scope:{} 屬性,就為這個 directive 創建出了一個隔離作用域。

示例5: directive 創建出一個孤立作用域

angular.module('isolate', []).directive("isolate", function () {
 return {
 scope : {},
 };
})

  獨立作用域最大的特點是不會原型繼承其父作用域,對外界的父作用域保持相對的獨立。因此,如果在定義了孤立作用域的 AngularJS directive 中想要訪問其父作用域的屬性,則得到的值為 undefined。代碼如下:

示例六:獨立作用域的隔離性

<!doctype html>
<html>
<head>
    <meta charset=utf-8"/>
    <title>scope nick</title>
    <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<script type="text/javascript">
    angular.module('app', [])
            .controller('ctrl', ['$scope', function($scope) {
                $scope.args = {};
            }])
            .directive("isolateDirective", function () {
                return {
                    scope : {},
                    link : function($scope, $element, $attr) {
                        console.log($scope.$args); //輸出 undefined
                    }
                };
            });
</script>
<body ng-app="app">
<div ng-controller="ctrl">
    <div isolate-directive></div>
</div>
</body>
</html>

  上面的代碼中通過在 directive 中聲明瞭 scope 屬性從而創建了一個作用域,其父作用域為 ctrl 所屬的作用域。但是,這個作用域是孤立的,因此,它訪問不到父作用域的中的任何屬性。存在這樣設計機制的好處是:能夠創建出一些列可復用的 directive,這些 directive 不會相互在擁有的屬性值上產生串擾,也不會產生任何副作用。

AngularJS 獨立作用域的數據綁定

  在繼承作用域中,我們可以選擇子作用域直接操作父作用域數據來實現父子作用域的通信,而在獨立作用域中,子作用域不能直接訪問和修改父作用域的屬性和值。為了能夠使孤立作用域也能和外界通信,AngularJS 提供了三種方式用來打破獨立作用域“孤立”這一限制。

單向綁定(@ 或者 @attr)

  這是 AngularJS 獨立作用域與外界父作用域進行數據通信中最簡單的一種,綁定的對象只能是父作用域中的字元串值,並且為單向只讀引用,無法對父作用域中的字元串值進行修改,此外,這個字元串還必須在父作用域的 HTML 節點中以 attr(屬性)的方式聲明。

  使用這種綁定方式時,需要在 directive 的 scope 屬性中明確指定引用父作用域中的 HTML 字元串屬性,否則會拋異常。示例代碼如下:

實例七: 單向綁定示例

<!doctype html>
<html>
<head>
    <meta charset=utf-8"/>
    <title>scope nick</title>
    <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<script>
    angular.module('isolateScope', [])
            .directive("isolateDirective", function () {
                return {
                    replace : true,
                    template: '<button>{{isolates}}</button>',
                    scope : {
                        isolates : '@',
                    },
                    link : function($scope, $element, $attr) {
                        $scope.isolates = "DeveloperWorks";//無效
                    }
                };
            })
            .controller("ctrl", function ($scope) {
                $scope.btns = 'NICK';
            });
</script>
<body ng-app="isolateScope" >
<div ng-controller="ctrl">
    <button>{{btns}}</button>
    <div isolate-directive isolates="{{btns}}"></div>
</div>
</body>
</html>

  上面的代碼,通過在 directive 中聲明瞭 scope:{isolates:'@'} 使得 directive 擁有了父作用域中 data-isolates (isolates為自定義屬性,不加data也可以,但建議加上data)這個 HTML 屬性所擁有的值,這個值在控制器 ctrl 中被賦值為'nick'。所以,代碼的運行結果是頁面上有兩個名為 nick的按鈕。

  我們還註意到 link 函數中對 isolates 進行了修改,但是最終不會在運行結果中體現。這是因為 isolates 始終綁定為父作用域中的 btns 字元串,如果父作用域中的 btns 不改變,那麼在孤立作用域中無論怎麼修改 isolates 都不會起作用。

引用綁定(&或者&attr)

  通過這種形式的綁定,孤立作用域將有能力訪問到父作用域中的函數對象,從而能夠執行父作用域中的函數來獲取某些結果。這種方式的綁定跟單向綁定一樣,只能以只讀的方式訪問父作用函數,並且這個函數的定義必須寫在父作用域 HTML 中的 attr(屬性)節點上。

  這種方式的綁定雖然無法修改父作用域的 attr 所設定的函數對象,但是卻可以通過執行函數來改變父作用域中某些屬性的值,來達到一些預期的效果。示例代碼如下:

示例八:引用綁定示例

<!doctype html>
<html>
<head>
    <meta charset=utf-8"/>
    <title>scope nick</title>
    <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<script>
    angular.module('isolateScope', [])
            .directive("isolateDirective", function () {
                return {
                    replace : true,
                    scope : {
                        isolates : '&',
                    },
                    link : function($scope, $element, $attr) {
                        var func = $scope.isolates();
                        func();
                    }
                };
            })
            .controller("ctrl", function ($scope) {
                $scope.func = function () {
                    console.log("Nick DeveloperWorks");
                }
            });
</script>
<body ng-app="isolateScope" >
<div ng-controller="ctrl">
    <div isolate-directive data-isolates="func"></div>
</div>
</body>
</html>

  這個例子中,瀏覽器的控制台將會列印“Nick DeveloperWorks”文字。

  上面的代碼中我們在父作用域中指定了一個函數對象$scope.func,在孤立作用域中通過對 HTML 屬性的綁定從而引用了 func。需要註意的是 link 函數中對 func 對象的使用方法,$scope.isolates 獲得的僅僅是函數對象,而不是調用這個對象,因此我們需要在調用完$scope.isolates 之後再調用這個函數,才能得到真正的執行結果。

雙向綁定(=或者=attr)

雙向綁定賦予 AngularJS 孤立作用域與外界最為自由的雙向數據通信功能。在雙向綁定模式下,孤立作用域能夠直接讀寫父作用域中的屬性和數據。和以上兩種孤立作用域定義數據綁定一樣,雙向綁定也必須在父作用域的 HTML 中設定屬性節點來綁定。

雙向綁定非常適用於一些子 directive 需要頻繁和父作用域進行數據交互,並且數據比較複雜的場景。不過,由於可以自由的讀寫父作用域中的屬性和對象,所以在一些多個 directive 共用父作用域數據的場景下需要小心使用,很容易引起數據上的混亂。

示例代碼如下:

示例九:雙向綁定示例

<!doctype html>
<html>
<head>
    <meta charset=utf-8"/>
    <title>scope nick</title>
    <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<script>
    angular.module('isolateScope', [])
            .directive("isolateDirective", function () {
                return {
                    replace : true,
                    template: '<button>{{isolates}}</button>',
                    scope : {
                        isolates : '=',
                    },
                    link : function($scope, $element, $attr) {
                        $scope.isolates.name = "NICK";
                    }
                };
            })
            .controller("ctrl", function ($scope) {
                $scope.btns = {
                    name : 'nick',
                    dw : 'DeveloperWorks'
                };
            });
</script>
<body ng-app="isolateScope" >
<div ng-controller="ctrl">
    <button>{{btns.dw}}</button>
    <button>{{btns.name}}</button>
    <div isolate-directive data-isolates="btns"></div>
</div>
</body>
</html>

  上面的代碼運行的結果是瀏覽器頁面上出現三個按鈕,其中第一個按鈕標題為“DeveloperWorks”,第二和第三個按鈕的標題為“NICK”。

初始時父作用域中的$scope.btns.name為小寫的“nick”,通過雙向綁定,孤立作用域中將父作用域的 name改寫成為大寫的“NICK”並且直接生效,父作用域的值被更改。

  推薦:這篇關於作用域的文章也寫的不錯,可以看看!

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這篇文章是對於編程小白新手來說的,對於已經在編程路上蹦躂的菜鳥,老鳥,大牛,請忽略~~哈哈! 1.打字速度是必備的素養! a) 打字速度是必備的素養! b) 可以使用市面上常見的打字練習工具,比如金山打字通等.不要練習中文打字,只練習英文打字就行了。英文文章一定要練習到每分鐘100字母以上。 2.認 ...
  • checkbox多選 技術一般水平有限,有什麼錯的地方,望大家指正。 全選,多選都是為了使用的方便,一般情況下全選就夠用了,但是用戶要求實現一個多選的功能也沒有辦法老老實實的做吧。 多選的實現也較為簡單,首先需要一個遮罩可以標識給用戶當前所選擇的區域,其次就是選中用戶所需要的信息,按照這個思路我們來 ...
  • 在Vue.js中,每一個vue文件都是一個組件,在.vue文件中可以將模板,腳本,樣式寫在一起,便於組織整個組件。在使用template,script時,編寫css樣式時,都進行的特別順利,唯獨當我想用sass來預處理css時,在style下使用lang='sass'一直報錯。 在.vue中是這樣的 ...
  • 本文實例為大家講解了jQuery $.ajax()方法參數,供大家參考,具體內容如下 $.ajax({ url:'test.do', data:{id:123,name:'xiaoming'}, type:'post', dataType:'json', success:function(data) ...
  • 由於html是從上至下載入的,通常我們如果在head部分引入javascript文件,那麼我們都會在javascript的開頭添加window.onload事件,防止在文檔問載入完成時進行DOM操作所出現的錯誤。如果有多個javascript文件,那麼極有可能出現多個window.onload事件, ...
  • 因集團的組織結構比較特別,為了很好的體現層級,又擺放美觀,自己謝了一個簡單的頁面,有需要的可供參考。 ...
  • 之前根據網易前端微專業的課程,寫了博客 水平居中 方案 垂直居中 方案 水平居中和垂直居中 方案 在最後,老師總結了『解決方案』提出的思路 比如說用到的 display : flex display:table display: inline-block display : table-cell v... ...
  • 單擊頁面任何地方關閉隱藏層的一種新的實現方法,有需要得朋友可以參考對比一下,可以自己在此基礎上擴展相關功能。(也可以在框架頁中綁定單擊事件,自己加一下即可。) ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...