ng-controller

ngController 会在视图上添加一个控制器类。这是 angular 如何支持 MVC(Model-View-Controller)设计模式原则的关键因素。

angular 中的 MVC 组件:

  • Model — 模型是作用域(scope)的属性;作用域依附在 DOM 元素上,作用域的属性则可以通过绑定被访问到。
  • View — 被渲染到视图(View)中的模板(拥有数据绑定的 HTML)
  • ControllerngController 指令指定了一个控制器(Controller)类; 这个类包含了应用(app)使用函数和值来修饰作用域背后的业务逻辑

注意:通过使用 $route 服务,你可以将控制器声明到路由定义中从而使该控制器关联到 DOM 上。

一个常见的错误:使用 ng-controller 再一次把控制器声明在模板上,这会导致控制器被设置和执行两次。

指令信息

这个指令创建了新的作用域 这个指令的执行优先级为500级

用法

用作属性:

<ANY ng-controller="expression">
...
</ANY>

参数

参数 形式 详细
ngController expression 使用当前 $controllerProvider 注册的构造函数的名字,或者是当前作用域上的一个表达式解析出的构造函数的名字。
通过指定 ng-controller="as propertyName" 将控制器实例发布到一个作用域(scope)属性中。
如果当前的 $controllerProvider 配置为使用全局(通过使用 $controllerProvider.allowGlobals()),这也会成为一个全局性的可访问的构造函数的名字(不推荐这么做)。

例子

这是一个简单的编辑用户联系信息的表。添加,删除,清空和展示方法被生命在控制器上(见源标签)。 这些方法可以轻易地从 angular 标记中调用。任何对数据的操作都会自动映射到视图中而不需要手动去更新。

下面介绍了两种不同风格的声明方式:

一种是将方法和属性直接绑定到控制器上,像这样:ng-controller="SettingsController1 as settings"

一种是将 $scope 注入到控制器中:ng-controller="SettingsController2"

第二种方式在 angular 社区中更为流行,而且在文档中也更经常使用这种方式作为样板。但是, 避免使用作用域而直接将属性绑定到控制器上还是有很多优势的。

  • 当多个控制器作用在一个元素上时,使用 controller as 让被访问模板上的控制器容易识别出来。
  • 如果你将你的控制器写成类的方式,则你会更方便的访问那些作用域中和控制器代码中的属性和方法
  • 由于绑定中总是有一个 .,所以你没有必要担心原型继承会覆盖原始代码。

这个例子展示了 controller as 的句法。

index.html

<div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
  <label>Name: <input type="text" ng-model="settings.name"/></label>
  <button ng-click="settings.greet()">greet</button><br/>
  Contact:
  <ul>
    <li ng-repeat="contact in settings.contacts">
      <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
         <option>phone</option>
         <option>email</option>
      </select>
      <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
      <button ng-click="settings.clearContact(contact)">clear</button>
      <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
    </li>
    <li><button ng-click="settings.addContact()">add</button></li>
 </ul>
</div>

app.js

angular.module('controllerAsExample', [])
  .controller('SettingsController1', SettingsController1);

function SettingsController1() {
  this.name = "John Smith";
  this.contacts = [
    {type: 'phone', value: '408 555 1212'},
    {type: 'email', value: '[email protected]'} ];
}

SettingsController1.prototype.greet = function() {
  alert(this.name);
};

SettingsController1.prototype.addContact = function() {
  this.contacts.push({type: 'email', value: '[email protected]'});
};

SettingsController1.prototype.removeContact = function(contactToRemove) {
 var index = this.contacts.indexOf(contactToRemove);
  this.contacts.splice(index, 1);
};

SettingsController1.prototype.clearContact = function(contact) {
  contact.type = 'phone';
  contact.value = '';
};

protractor.js

it('should check controller as', function() {
  var container = element(by.id('ctrl-as-exmpl'));
    expect(container.element(by.model('settings.name'))
      .getAttribute('value')).toBe('John Smith');

  var firstRepeat =
      container.element(by.repeater('contact in settings.contacts').row(0));
  var secondRepeat =
      container.element(by.repeater('contact in settings.contacts').row(1));

  expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
      .toBe('408 555 1212');

  expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
      .toBe('[email protected]');

  firstRepeat.element(by.buttonText('clear')).click();

  expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
      .toBe('');

  container.element(by.buttonText('add')).click();

  expect(container.element(by.repeater('contact in settings.contacts').row(2))
      .element(by.model('contact.value'))
      .getAttribute('value'))
      .toBe('[email protected]');
});

这个例子展示的是控制器的 “附属到 $scope 上” 形式。

index.html

<div id="ctrl-exmpl" ng-controller="SettingsController2">
  <label>Name: <input type="text" ng-model="name"/></label>
  <button ng-click="greet()">greet</button><br/>
  Contact:
  <ul>
    <li ng-repeat="contact in contacts">
      <select ng-model="contact.type" id="select_{{$index}}">
         <option>phone</option>
         <option>email</option>
      </select>
      <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
      <button ng-click="clearContact(contact)">clear</button>
      <button ng-click="removeContact(contact)">X</button>
    </li>
    <li>[ <button ng-click="addContact()">add</button> ]</li>
 </ul>
</div>

app.js

angular.module('controllerExample', [])
  .controller('SettingsController2', ['$scope', SettingsController2]);

function SettingsController2($scope) {
  $scope.name = "John Smith";
  $scope.contacts = [
    {type:'phone', value:'408 555 1212'},
    {type:'email', value:'[email protected]'} ];

  $scope.greet = function() {
    alert($scope.name);
  };

  $scope.addContact = function() {
    $scope.contacts.push({type:'email',¡ value:'[email protected]'});
  };

  $scope.removeContact = function(contactToRemove) {
    var index = $scope.contacts.indexOf(contactToRemove);
    $scope.contacts.splice(index, 1);
  };

  $scope.clearContact = function(contact) {
    contact.type = 'phone';
    contact.value = '';
  };
}

protractor.js

it('should check controller', function() {
  var container = element(by.id('ctrl-exmpl'));

  expect(container.element(by.model('name'))
      .getAttribute('value')).toBe('John Smith');

  var firstRepeat =
      container.element(by.repeater('contact in contacts').row(0));
  var secondRepeat =
      container.element(by.repeater('contact in contacts').row(1));

  expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
      .toBe('408 555 1212');
  expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
      .toBe('[email protected]');

  firstRepeat.element(by.buttonText('clear')).click();

  expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
      .toBe('');

  container.element(by.buttonText('add')).click();

  expect(container.element(by.repeater('contact in contacts').row(2))
      .element(by.model('contact.value'))
      .getAttribute('value'))
      .toBe('[email protected]');
});