七步從Angular.JS菜鳥(niǎo)到專(zhuān)家(2):Scopes
這是"AngularJS - 七步從菜鳥(niǎo)到專(zhuān)家"系列的第二篇。
在***篇我們展示了如何開(kāi)始搭建一個(gè)Angular應(yīng)用。在這一篇里,我們要討論一個(gè)理解AngularJS運(yùn)作原理所必須的基本概念,以及你如何更好地運(yùn)用它。
在這個(gè)系列教程里,我們會(huì)開(kāi)發(fā)一個(gè)NPR(美國(guó)全國(guó)公共廣播電臺(tái))廣播的音頻播放器,它能顯示Morning Edition節(jié)目里現(xiàn)在播出的***故事,并在我們的瀏覽器里播放。完成版的Demo可以看這里。
第二部分 Scopes
$scope是一個(gè)把view(一個(gè)DOM元素)連結(jié)到controller上的對(duì)象。在我們的MVC結(jié)構(gòu)里,這個(gè) $scope 將成為model,它提供一個(gè)綁定到DOM元素(以及其子元素)上的excecution context。
盡管聽(tīng)起來(lái)有點(diǎn)復(fù)雜,但 $scope 實(shí)際上就是一個(gè)JavaScript對(duì)象,controller和view都可以訪問(wèn)它,所以我們可以利用它在兩者間傳遞信息。在這個(gè) $scope 對(duì)象里,我們既存儲(chǔ)數(shù)據(jù),又存儲(chǔ)將要運(yùn)行在view上的函數(shù)。
每一個(gè)Angular應(yīng)用都會(huì)有一個(gè) $rootScope。這個(gè) $rootScope 是最***的scope,它對(duì)應(yīng)著含有 ng-app 指令屬性的那個(gè)DOM元素。
如果頁(yè)面上沒(méi)有明確設(shè)定 $scope ,Angular 就會(huì)把數(shù)據(jù)和函數(shù)都綁定到這里, ***部分中的例子就是靠這一點(diǎn)成功運(yùn)行的。
在這個(gè)例子里,我們將使用 $rootScope 。在main.js文件里,我們給這個(gè)scope加一個(gè)name屬性。把這個(gè)函數(shù)放進(jìn) app.run函數(shù)里執(zhí)行,我們就保證了它能在應(yīng)用的其他部分之前被執(zhí)行。你可以把a(bǔ)pp.run函數(shù)看作是Angular應(yīng)用的main方法。
- app.run(function($rootScope) {
- $rootScope.name = "Ari Lerner";
- });
現(xiàn)在,我們可以在view的任何地方訪問(wèn)這個(gè)name屬性,使用模版表達(dá)式{{}},像這樣:
- {{ name }}
在這個(gè)系列之后的章節(jié)里,我們會(huì)深入介紹模版表達(dá)式的語(yǔ)法。
請(qǐng)看:
Ari Lerner
總之:
要真正看到scope的強(qiáng)大功能,讓我們給一個(gè)DOM元素加上controller,它將創(chuàng)建這個(gè)元素的$scope ,讓我們跟這個(gè)元素互動(dòng)。
ng-controller
要明確創(chuàng)建一個(gè)$scope 對(duì)象,我們就要給DOM元素安上一個(gè)controller對(duì)象,使用的是ng-controller 指令屬性:
- <div ng-controller="MyController">
- {{ person.name }}
- </div>
g-controller指令給所在的DOM元素創(chuàng)建了一個(gè)新的$scope 對(duì)象,并將這個(gè)$scope 對(duì)象包含進(jìn)外層DOM元素 的$scope 對(duì)象里。在上面的例子里,這個(gè)外層DOM元素的$scope 對(duì)象,就是$rootScope 對(duì)象。這個(gè)scope鏈?zhǔn)沁@樣的:
現(xiàn)在,MyController 給我們建立了一個(gè)可以從DOM元素內(nèi)部直接訪問(wèn)的$scope 對(duì)象。下面我們?cè)诘倪@個(gè)$scope 里創(chuàng)建一個(gè)person對(duì)象,在main.js中:
- app.controller('MyController', function($scope) {
- $scope.person = {
- name: "Ari Lerner"
- };
- });
現(xiàn)在我們可以在有ng-controller=’MyController’屬性的DOM元素的任何子元素里訪問(wèn)這個(gè)person 對(duì)象,因?yàn)樗?scope上。
請(qǐng)看:
Ari Lerner
除了一個(gè)例外,所有scope都遵循原型繼承(prototypal inheritance),這意味著它們都能訪問(wèn)父scope們。對(duì)任何屬性 和方法,如果AngularJS在當(dāng)前scope上找不到,就會(huì)到父scope上去找,如果在父scope上也沒(méi)找到,就會(huì)繼續(xù)向上回溯,一直 到$rootScope 上。
唯一的例外:有些指令屬性可以選擇性地創(chuàng)建一個(gè)獨(dú)立的scope,讓這個(gè)scope不繼承它的父scope們。
舉個(gè)例子,假設(shè)我們有一個(gè)ParentController ,含有一個(gè)person 對(duì)象,又有一個(gè)ChildController 想要訪問(wèn)這個(gè)對(duì)象:
- app.controller('ParentController', function($scope) {
- $scope.person = {greeted: false};
- });
- app.controller('ChildController', function($scope) {
- $scope.sayHello = function() {
- $scope.person.greeted = true;
- }
- });
當(dāng)我們?cè)趘iew里把ChildController 綁定到ParentController 之下,在子元素里我們就能訪問(wèn)ParentController 創(chuàng)建的父scope的屬性,像訪問(wèn)ChildController 自己的scope中的屬性一樣:
- <div ng-controller="ParentController">
- <div ng-controller="ChildController">
- <input type="text" ng-model="person.name" placeholder="Name"></input>
- <a ng-click="sayHello()">Say hello</a>
- </div>
- {{ person }}
- </div>
#p#
請(qǐng)看:

結(jié)合進(jìn)myApp
現(xiàn)在,我們把 $scope 用在我們的NPR應(yīng)用上。在上一篇結(jié)尾我們定義了app module,現(xiàn)在我們開(kāi)始深入DOM結(jié)構(gòu),創(chuàng)建基本功能。
像在上面的例子里展示過(guò)的那樣,我們先創(chuàng)建一個(gè)root controller,命名為PlayerController。還有一個(gè) RelatedController ,它將負(fù)責(zé)管理音頻DOM元素、和為我們?nèi)』豊PR節(jié)目的列表。
回到main.js,現(xiàn)在我們就來(lái)創(chuàng)建這兩個(gè)controller:
- var app = angular.module('myApp', []);
- app.controller('PlayerController', ['$scope', function($scope) {
- }]);
- app.controller('RelatedController', ['$scope', function($scope) {
- }]);
音頻
這兩個(gè)controller現(xiàn)在還沒(méi)什么功能,那么,讓我們給應(yīng)用先加上點(diǎn)聲音吧。在這個(gè)教程里我們將使用HTML5的音頻DOM元素,所以首先你得有個(gè)支持HTML5的瀏覽器(我們推薦Google Chrome)。
這個(gè)音頻DOM元素,我們既可以把它加在HTML里,又可以加在我們的controller里。不過(guò)鑒于我們主要使用controller跟這個(gè)音頻DOM元素互動(dòng),把它創(chuàng)建在controller里更合適。
現(xiàn)在我們就在PlayerController里創(chuàng)建一個(gè)音頻DOM元素。我們要把它儲(chǔ)存在scope上,然后——像你已經(jīng)學(xué)過(guò)的那樣——通過(guò)$scope對(duì)象把view和controller連接起來(lái)。
- app.controller('PlayerController', ['$scope', function($scope) {
- $scope.audio = document.createElement('audio');
- }]);
這個(gè)設(shè)定現(xiàn)在可能有點(diǎn)無(wú)聊,因?yàn)樗€不能干什么。我們會(huì)在本系列的下一篇介紹“取回(fetching)”數(shù)據(jù),現(xiàn)在我們先使用一個(gè)指定的.mp4網(wǎng)址。
還是在這個(gè)PlayerController里,指定音頻文件的src屬性為一個(gè)你能訪問(wèn)的.mp4網(wǎng)址。方便起見(jiàn),我們?cè)谶@里使用一個(gè)儲(chǔ)存在我們自己服務(wù)器上的NPR音頻文件,不過(guò)其實(shí)你可以指向任何網(wǎng)址?,F(xiàn)在設(shè)定你的音頻src地址如下:
- app.controller('PlayerController', ['$scope', function($scope) {
- $scope.playing = false;
- $scope.audio = document.createElement('audio');
- $scope.audio.src = '/media/npr.mp4';
- }]);
試試看
Play Playing audio: false (請(qǐng)到英文原文測(cè)試“播放”按鈕)
音頻不會(huì)自己播放,我們必須讓它播放。要做到這一點(diǎn),我們可以簡(jiǎn)單地使用$scope.audio.play(),然后HTML5音頻DOM元素就會(huì)開(kāi)始播放mp4媒體流。
我們可以給用戶提供一個(gè)互動(dòng)元素:創(chuàng)建一個(gè)按鈕,把它綁定到$scope里的一個(gè)動(dòng)作上。在下一篇里我們會(huì)更深入地介紹這一塊,不過(guò)先看看上面例子里view的HTML:
- <div ng-controller="PlayerController">
- <button ng-click="play()" class="button" ng-show="!playing">Play</button>
- <button ng-click="stop()" class="button alert" ng-show="playing">Stop</button>
- Playing audio: <b>{{ playing }}</b>
- </div>
注意我們并不需要引用在scope里創(chuàng)建的那個(gè)音頻DOM元素,因?yàn)樗钱?dāng)我們載入controller時(shí)在controller內(nèi)部用 document.createElement(“audio”)創(chuàng)建的。在之后的教程里我們會(huì)重構(gòu)這個(gè)部分,因?yàn)樵赾ontroller里操作DOM元 素一般都不是個(gè)好主意(感謝Brad Green在評(píng)論中指出這一點(diǎn)。)然而為了簡(jiǎn)便,我們?cè)谶@里還是保持這個(gè)controller如此。
在view里我們已經(jīng)加入了一些變量,在 $scope 上我們要管理這些變量。這里使用了一些高級(jí)概念,這些在本系列之后的教程里才會(huì)詳細(xì)介紹,所以如果你不能一下子全看明白也不用擔(dān)心:
- app.controller('PlayerController', ['$scope', function($scope) {
- $scope.playing = false;
- $scope.audio = document.createElement('audio');
- $scope.audio.src = '/media/npr.mp4';
- $scope.play = function() {
- $scope.audio.play();
- $scope.playing = true;
- };
- $scope.stop = function() {
- $scope.audio.pause();
- $scope.playing = false;
- };
- $scope.audio.addEventListener('ended', function() {
- $scope.$apply(function() {
- $scope.stop()
- });
- });
- }]);
以上就是對(duì)Angular.js的$scope 功能的介紹。在下一章,我們會(huì)介紹Angular.js的雙向數(shù)據(jù)綁定。
本系列的官方代碼庫(kù)可從github上下載:
https://github.com/auser/ng-newsletter-beginner-series.
要將這個(gè)代碼庫(kù)保存到本地,請(qǐng)先確保安裝了git,clone此代碼庫(kù),然后check out其中的part2分支:
- git clone https://github.com/auser/ng-newsletter-beginner-series.git
- git checkout -b part2
原文鏈接:http://www.ng-newsletter.com/posts/beginner2expert-scopes.html
譯文鏈接:http://blog.jobbole.com/48593/