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');
    });
});
相关文章
相关标签/搜索