瞭解控制器controller
在AngularJS中,Controller由JavaScript構造函數定義,用於擴充AngularJS Scope。
當控制器經過ng-controller指令鏈接到DOM時,AngularJS將使用指定的Controller的構造函數實例化一個新的Controller對象。 將建立一個新的子做用域,並將其做爲可注入參數提供給Controller的構造函數做爲$ scope。
若是控制器已使用控制器做爲語法附加,則控制器實例將被分配給新做用域上的屬性。
使用控制器:css
設置$scope對象的初始狀態。html
將行爲添加到$scope對象。app
不要使用控制器:函數
操做DOM - 控制器應該只包含業務邏輯。 將任何表示邏輯放入控制器顯着影響其可測性。 AngularJS有數據綁定大多數狀況下和指令封裝手動DOM操做。測試
格式輸入 - 使用AngularJS表單控件。code
過濾器輸出 - 使用AngularJS過濾器。htm
在控制器上共享代碼或狀態 - 使用AngularJS服務。對象
管理其餘組件的生命週期(例如,建立服務實例)。繼承
設置$scope對象的初始狀態
一般,當建立應用程序時,須要爲AngularJS $ scope設置初始狀態。 經過將屬性附加到$scope對象來設置做用域的初始狀態。 屬性包含視圖模型(將由視圖呈現的模型)。 全部$scope屬性將在控制器註冊的DOM中的點處可用於模板。
如下示例演示瞭如何建立一個GreetingController,它附加一個包含字符串'Hola!'的greeting屬性在$scope中:生命週期
var myApp = angular.module('myApp',[]); myApp.controller('GreetingController', ['$scope', function($scope) { $scope.greeting = 'Hola!'; }]);
咱們爲咱們的應用程序建立一個AngularJS模塊myApp。 而後咱們使用.controller()方法將控制器的構造函數添加到模塊中。 這將使控制器的構造函數保持在全局範圍以外。
咱們使用了一個內聯注入註釋來顯式地指定Controller對AngularJS提供的$scope服務的依賴。
咱們使用ng-controller指令將控制器附加到DOM。 greeting屬性如今能夠與數據綁定到模板:
<div ng-controller="GreetingController"> {{ greeting }} </div>
將行爲添加到scope對象
爲了對事件做出反應或在視圖中執行計算,咱們必須向scope提供行爲。 咱們經過將方法附加到$scope對象來將行爲添加到做用域。 而後能夠從模板/視圖調用這些方法。
如下示例使用Controller將一個數字加倍的方法添加到做用域:
var myApp = angular.module('myApp',[]); myApp.controller('DoubleController', ['$scope', function($scope) { $scope.double = function(value) { return value * 2; }; }]);
將控制器附加到DOM後,能夠在模板中的AngularJS表達式中調用double方法:
<div ng-controller="DoubleController"> Two times <input ng-model="num"> equals {{ double(num) }} </div>
分配給scope的任何對象(或基元)都將成爲模型屬性。 分配給做用域的任何方法都在模板/視圖中可用,而且能夠經過AngularJS表達式和ng事件處理程序指令(例如ngClick)調用。
正確使用控制器
通常來講,控制器不該該嘗試作太多。 它應該只包含單個視圖所需的業務邏輯。
保持Controllers精簡的最多見的方法是將不屬於控制器的工做封裝到服務中,而後經過依賴注入在Controllers中使用這些服務。
將控制器與AngularJS Scope對象相關聯
能夠經過ngController指令或$route服務隱式地將控制器與scopes對象相關聯。
簡單的控制器(spicy)示例
爲了進一步說明Controller組件如何在AngularJS中工做,讓咱們使用如下組件建立一個小應用程序:
有兩個按鈕和一個簡單的消息的模板
由一個名爲spice的字符串組成的模型
具備設置香料價值的兩個功能的控制器
咱們的模板中的消息包含對香料模型的綁定,默認狀況下,它設置爲字符串「very」。 根據單擊哪一個按鈕,香料模型設置爲辣椒或jalapeño,而且消息經過數據綁定自動更新。
index.html
<div ng-controller="SpicyController"> <button ng-click="chiliSpicy()">Chili</button> <button ng-click="jalapenoSpicy()">Jalapeño</button> <p>The food is {{spice}} spicy!</p> </div>
app.js
var myApp = angular.module('spicyApp1', []); myApp.controller('SpicyController', ['$scope', function($scope) { $scope.spice = 'very'; $scope.chiliSpicy = function() { $scope.spice = 'chili'; }; $scope.jalapenoSpicy = function() { $scope.spice = 'jalapeño'; }; }]);
在上面的例子注意事項:
ng-controller指令用於(隱式地)爲模板建立範圍,而且scope由SpicyController控制器擴充(管理)。
SpicyController只是一個純JavaScript函數。 做爲(可選)命名約定,名稱以大寫字母開頭,以"Controller"結尾。
將屬性分配給$scope建立或更新模型。
控制器方法能夠經過直接賦值到scope來建立(參見chiliSpicy方法)
Controller方法和屬性在模板中可用(對於<div>元素及其子元素)。
參數示例
控制器方法也能夠接受參數,如前面示例的如下變體所示。
index.html
<div ng-controller="SpicyController"> <input ng-model="customSpice"> <button ng-click="spicy('chili')">Chili</button> <button ng-click="spicy(customSpice)">Custom spice</button> <p>The food is {{spice}} spicy!</p> </div>
app.js
var myApp = angular.module('spicyApp2', []); myApp.controller('SpicyController', ['$scope', function($scope) { $scope.customSpice = 'wasabi'; $scope.spice = 'very'; $scope.spicy = function(spice) { $scope.spice = spice; }; }]);
SpicyController控制器如今只定義一個名爲spicy的方法,它接受一個名爲spice的參數。 模板而後引用此Controller方法,並在第一個按鈕的綁定中傳遞字符串常量「chili」,並在第二個按鈕中傳遞模型屬性customSpice(綁定到輸入框)。
scope繼承示例
一般將控制器附加在DOM層次結構的不一樣級別。 因爲ng-controller指令建立了一個新的子範圍,咱們獲得了一個繼承的範圍的層次結構。 每一個控制器接收的$scope將能夠訪問由層次結構中較高的控制器定義的屬性和方法。
index.html
<div class="spicy"> <div ng-controller="MainController"> <p>Good {{timeOfDay}}, {{name}}!</p> <div ng-controller="ChildController"> <p>Good {{timeOfDay}}, {{name}}!</p> <div ng-controller="GrandChildController"> <p>Good {{timeOfDay}}, {{name}}!</p> </div> </div> </div> </div>
app.css
div.spicy div { padding: 10px; border: solid 2px blue; }
app.js
var myApp = angular.module('scopeInheritance', []); myApp.controller('MainController', ['$scope', function($scope) { $scope.timeOfDay = 'morning'; $scope.name = 'Nikki'; }]); myApp.controller('ChildController', ['$scope', function($scope) { $scope.name = 'Mattie'; }]); myApp.controller('GrandChildController', ['$scope', function($scope) { $scope.timeOfDay = 'evening'; $scope.name = 'Gingerbread Baby'; }]);
注意咱們如何在模板中嵌套三個ng-controller指令。 這將致使爲咱們的視圖建立四個範圍:
rootscope(根做用域)
MainController scope,其中包含timeOfDay和name屬性
ChildController scope,它繼承timeOfDay屬性,但覆蓋(陰影)來自上一個scope的name屬性
GrandChildController scope,它覆蓋(陰影)在MainController中定義的timeOfDay屬性和在ChildController中定義的name屬性
繼承以與對屬性的方式相同的方式處理方法。 因此在咱們之前的例子中,全部的屬性均可以替換爲返回字符串值的方法。
測試控制器
雖然有不少方法來測試一個控制器,最好的約定之一,以下所示,涉及注入$rootScope和$controller:
控制器定義:
var myApp = angular.module('myApp',[]); myApp.controller('MyController', function($scope) { $scope.spices = [{"name":"pasilla", "spiciness":"mild"}, {"name":"jalapeno", "spiciness":"hot hot hot!"}, {"name":"habanero", "spiciness":"LAVA HOT!!"}]; $scope.spice = "habanero"; });
控制器測試
describe('myController function', function() { describe('myController', function() { var $scope; beforeEach(module('myApp')); beforeEach(inject(function($rootScope, $controller) { $scope = $rootScope.$new(); $controller('MyController', {$scope: $scope}); })); it('should create "spices" model with 3 spices', function() { expect($scope.spices.length).toBe(3); }); it('should set the default value of spice', function() { expect($scope.spice).toBe('habanero'); }); }); });
若是須要測試嵌套的控制器,則必須在DOM中存在的測試中建立相同的做用域層次結構:
describe('state', function() { var mainScope, childScope, grandChildScope; beforeEach(module('myApp')); beforeEach(inject(function($rootScope, $controller) { mainScope = $rootScope.$new(); $controller('MainController', {$scope: mainScope}); childScope = mainScope.$new(); $controller('ChildController', {$scope: childScope}); grandChildScope = childScope.$new(); $controller('GrandChildController', {$scope: grandChildScope}); })); it('should have over and selected', function() { expect(mainScope.timeOfDay).toBe('morning'); expect(mainScope.name).toBe('Nikki'); expect(childScope.timeOfDay).toBe('morning'); expect(childScope.name).toBe('Mattie'); expect(grandChildScope.timeOfDay).toBe('evening'); expect(grandChildScope.name).toBe('Gingerbread Baby'); }); });