Skip to content

Latest commit

 

History

History
277 lines (209 loc) · 9.5 KB

03控制器.md

File metadata and controls

277 lines (209 loc) · 9.5 KB

理解控制器

在Angular中,控制器是由JavaScript构造函数定义的,参数是域(scope).

当一个控制器通过ng-controller指令附加到DOM元素时,Angular将会实例化一个新的控制器对象,使用指定的控制器构造函数.一个新的子作用域会被创建并成为一个可注入的参数,名称为$scope.

如果控制器通过使用controller as语法附加到DOM元素时,控制器的实例将被复制到新作用域的一个属性上.

使用控制器:

  • 设置$scope对象的初始状态
  • $scope对象增加行为

不要使用控制器:

  • 操作DOM--控制器应该只包含业务逻辑.增加任何显示逻辑到控制器上会显著影响控制器的可测试性.Angular拥有数据绑定和指令去封装DOM操作.
  • 格式化输入--应该使用Angular表单控制.
  • 过滤输出--使用过滤器代替.
  • 通过控制器共享代码和状态--使用Angular的自定义服务代替.
  • 管理其他组件的生命周期(例如创建服务的实例)

设置scope对象的初始状态

当你创建一个应用程序时,你需要为你的$scope对象设置初始状态.你可以通过给$scope添加新的属性去设置初始状态.这些属性包含了view model(模型会通过视图显示出来).所有的$scope属性可以在控制器注册的DOM元素和子元素上使用.

接下来的例子展示了创建一个GreetingController控制器,并添加了一个值为Hola!greeting属性到$scope:

var myApp = angular.module('myApp',[]);

myApp.controller('GreetingController', ['$scope', function($scope) {
  $scope.greeting = 'Hola!';
}]);

我们为我们的程序创建了一个Angular模块myApp.然后我们使用.controller()方法将控制器的构造函数添加到模块上.使得控制器的构造函数没有暴露在全局作用域.

我们使用内联的数组注解去显示的指定控制器对由Angular提供的$scope服务的依赖.查看依赖注入章节获得更多的信息.

我们通过使用ng-controller指令将控制器附加到DOM元素上.greeting属性能被数据绑定到模板上.

<div ng-controller="GreetingController">
  {{ greeting }}
</div>

为scope添加行为

为了响应时间或者在视图中执行计算,我们必须为$scope提供行为.我们可以通过给$scope对象添加方法的方式去为scope添加行为.这些方法可以在视图/模板中调用.

接下来的例子使用了一个控制器去添加方法,使数字翻倍:

var myApp = angular.module('myApp',[]);

myApp.controller('DoubleController', ['$scope', function($scope) {
  $scope.double = function(value) { return value * 2; };
}]);

一旦控制器被附加到DOM元素上,这个double函数可以在模板中以Angular表达式的方式执行:

<div ng-controller="DoubleController">
  Two times <input ng-model="num"> equals {{ double(num) }}
</div>

像在概念那一章讨论的一样,任何对象或基本数据类型可以成为scope的模型属性.任何scope上的方法可以在视图/模板上使用,并且可以通过angular表达式和ng事件处理器指令执行(如ng-click)

正确使用控制器

通常来说,一个控制器不应该去处理过多的逻辑.它应该只包含单一视图的必要业务逻辑.

最普遍的方式去保持控制器的规模小,是通过封装不属于控制器的工作到服务中,然后在控制器中通过依赖注入去使用这些服务.

关联控制器和scope对象

一可以隐式的关联控制器和scope对象通过ng-controller指令或者$route服务.


简单的辣控制器实例

为了阐述控制器组件如何在Angular运作,我们创建一个拥有以下组件的小应用:

  • 一个带有两个按钮和一个简单消息的模板
  • 一个由名称为spice的字符串组成的模型
  • 一个有两个为spice设置值的函数的控制器

在模版中的消息包含了一个spice的绑定.默认的会被设置为"very".spice模型会被设置为chili或者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,并且这个scope被SpicyController控制器管理.
  • SpicyController只是一个简单的Javascript函数.一个可选的命名规范,首字母大写切实用Controller作为后缀.
  • 通过分配一个属性给$scope去创建或更新模型
  • 控制器方法可以通过直接给scope赋值的方式创建
  • 控制器方法和属性可以在模版中使用(包括<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控制器现在只定义了一个带有一个spice参数的spicy方法.之后模板引用了这个方法.第一个按钮传递了一个常量chili,另一个按钮则传递了一个模型属性cunstomSpice(绑定在input标签上).

##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指令是如何在模板中嵌套的.这将导致在我们的视图中创建四个作用域(scope):

  • 根作用域
  • MainController的作用域,包含了timeOfDayname属性
  • ChildController作用域,继承了timeOfDay属性但是覆盖了之前的name属性
  • GrandChildController作用域,定义在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');
    });
  });
});

如果你需要测试一个嵌套的控制器,你就必须在测试中创建一个相同的被继承的作用域.

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');
    });
});

翻译人:刘能