Angular開發者指南(四)控制器

瞭解控制器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');
    });
});
相關文章
相關標籤/搜索