model
model
这个词在AngularJS中,既可以表示一个(比如,一个叫做phones
的model
,它的值是一个包含多个phone
的数组)对象,也可以表示应用中的整个数据模型,这取决于我们所讨论的AngularJS文档中的上下文。
在AngularJS中,一个模型就是AngularJS作用域对象中的任何一个可取的属性。属性的名字就是模型的标示符。它的值可以是任意的Javascript对象(包括数组和原始对象)。
将Javascript对象变成模型的唯一要求是这个对象必须是AngularJS作用域的一个属性引用。这个引用既可以显式也可以隐式地创建。
你可以像下面这样显式地创建一个作用域属性,引用Javascript对象:
- 在Javascript代码中直接将一个对象赋给作用域对象属性;这种情况常见于控制器中:
function MyCtrl($scope) { // create property ‘foo‘ on the MyCtrl‘s scope // and assign it an initial value ‘bar‘ $scope.foo = ‘bar‘; }
- 在模板中使用表达式:
<button ng-click="{{foos=‘ball‘}}">Click me</button>
- 在模板中使用
ngInit
指令(只适用于实例,不推荐在实际应用中使用):<body ng-init=" foo = ‘bar‘ ">
当处理下面这样的模板结构时,AngularJS会隐式地(通过创建一个作用域对象的属性,并将合适地值赋给它来实现)创建模型。
- 从input, select, textarea或者其他表单元素中:
<input ng-model="query" value="fluffy cloud">
上面的代码,在当前作用域中创建了一个叫做query
的模型,值被设置成"fluffy cloud"。
- 在
ngRepeater
的迭代声明中:<p ng-repeat="phone in phones"></p>
上面代码,为phones
数组中的每一项都创建了一个子作用域。并且在各自的作用域中创建了一个叫做phone
的对象(模型),它的值被设置成数组中当前的值。
Controller
在AngularJS中,控制器是一个Javascript函数,用来增强除了根作用域以外的作用域实例的。当你或者AngularJS本身通过scope.$new
创建一个新的子作用域对象时,有一个选项能让你将它当做参数传递给控制器。这能使AngularjS将控制器和这个作用域联系起来,增强作用域的行为。
控制器用于:
- 设置好作用域对象的初始状态。
- 给作用域对象增加行为。
给作用于对象设置初始状态:
一般来说,当你创建应用时,你需要对它的作用域设置初始状态。
AngularJS将对作用域对象调用控制器的构造函数(从某种意义上来说就像使用Javascript的apply方法一样),以此来设置作用域的初始状态。这意味着AngularJS不会创建控制器类型的实例(不会使用new方法来调用控制器构造函数)。控制器总是对某个已存在的作用域对象调用。
你可以通过创建一个模型属性来设置初始作用域的初始状态。 比如:
function GreetingCtrl($scope) { $scope.greeting = ‘Hola!‘; }
GreetingCtrl控制器,创建了一个模板中可以调用的,叫greeting
的模型。
给作用域对象增加行为
AngularJS作用域对象的行为是由作用域的方法来表示的。这些方法是可以在模板或者说视图中调用的。这些方法和应用的模型交互,并且能改变模型。
如我们在模型那一章所说的,任何对象(或者原生的类型)被赋给作用域后就会变成模型。任何赋给作用域的方法,都能在模板或者说视图中被调用,并且能通过表达式或者ng
事件指令调用。(比如,ngClick)
我们如何正确地使用控制器呢?
总的来说,一个控制器不应该做太多工作。它应该只包含单个视图的业务逻辑。
保持控制器职责单一的最常见做法是将那些不属于控制器的工作抽离到服务中,然后通过依赖注入,在控制器中使用这些服务。这在依赖注入服务的章节中会详细讨论。
不要用控制器干下面的事情:
- 控制器应该只关心业务逻辑。DOM操作(表现层逻辑)通常会把测试弄得很难。将任何表现层逻辑放到控制器中都会显著地增加对业务逻辑的测试难度。AngularJS提供
dev_guide.templates.databinding
用来自动进行DOM操作。如果你需要手动操作DOM,将表现层的逻辑抽离到指令中。 - 对输入格式化 — 你应该用AngularJS的表单控制来实现格式化。.
- 对输出格式化 — 你应该用AngularJS的过滤器实现。.
- 实例化组件或者控制其它组件的生命周期(比如创建一个服务的实例).
你可以显示地用scope.$new
来将控制器和作用域对象显示地联系起来,或者隐式地通过ngController
指令或者$route
服务来联系。
最后,我们用实例来讲解controller:
为了阐述AngularJS的控制器组件的运行原理,让我们来创建一个拥有下面这些组件的小应用:
- 一个有两个按钮和一条消息的模板
- 一个叫
spice
的字符串模型。 - 一个拥有两个方法的控制器SpicyCtrl。这两个方法是用来设置
spice
的值。
<body ng-controller="SpicyCtrl"> <button ng-click="chiliSpicy()">Chili</button> <button ng-click="jalapenoSpicy()">Jalapeño</button> <p>The food is {{spice}} spicy!</p> </body> function SpicyCtrl($scope) { $scope.spice = ‘very‘; $scope.chiliSpicy = function() { $scope.spice = ‘chili‘; } $scope.jalapenoSpicy = function() { $scope.spice = ‘jalapeño‘; } }
模板中的消息(p元素中的内容)包含了一个对spice
模型的绑定,它初始的字符串是“very”。这个spice模型会被设置成 chili 或者 jalapeno,这取决于哪个按钮会被点击。消息会通过数据绑定
自动更新。例子中有下面这些需要注意:
ngController
指令是用来(隐式地)为模板创建作用域的。并且使用指令中指定的spicyCtrl
控制器来增强这个作用域。- 对作用域对象赋予一个新的属性会创建或者更新模型。
- 控制器中的所用方法都能在模板中调用(在body元素或者子元素中).
- AngularJS的老版本(1.0RC之前的)会自动给作用域对象原型添加方法,现在不会了。所有的方法都必须手动添加到作用域。
控制器方法可以接受参数,比如:
<body ng-controller="SpicyCtrl"> <input ng-model="customSpice" value="wasabi"> <button ng-click="spicy(‘chili‘)">Chili</button> <button ng-click="spicy(customSpice)">Custom spice</button> <p>The food is {{spice}} spicy!</p> </body> function SpicyCtrl($scope) { $scope.spice = ‘very‘; $scope.spicy = function(spice) { $scope.spice = spice; } }
在SpicyCtrl
控制器中,只定义了一个叫spicy的方法,它接受一个叫做spice的参数。和这个控制器相关的模板在第一个按钮事件中传递了一个chili
常量给控制器中的方法,在第二个按钮中传递一个模型属性。
控制器继承示例:
AngularJS中的控制器继承是基于作用域的继承的,比如:
<body ng-controller="MainCtrl"> <p>Good {{timeOfDay}}, {{name}}!</p> <div ng-controller="ChildCtrl"> <p>Good {{timeOfDay}}, {{name}}!</p> <p ng-controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p> </body> function MainCtrl($scope) { $scope.timeOfDay = ‘morning‘; $scope.name = ‘Nikki‘; } function ChildCtrl($scope) { $scope.name = ‘Mattie‘; } function BabyCtrl($scope) { $scope.timeOfDay = ‘evening‘; $scope.name = ‘Gingerbreak Baby‘; }
注意我们是如何在模板中嵌套我们的ngController
指令的。这个模板结构会使得AngularJS为视图创建四个作用域:
- 根作用域
- MainCtrl作用域, 它包含了模型timeOfDay和模型name。
- ChildCtrl作用域,它继承了上层作用域的timeOfDay,复写了name。
- BabyCtrl作用域,复写了MainCtrl中定义的timeOfDay和ChildCtrl中的name。
控制器的继承和模型继承是同一个原理。
注意:常规的原型继承对控制器来说不起作用。因为正如我们之前提到的,控制器不是直接实例化的,而是对作用域对象调用的。
View
在AngularJS中,视图(view)指的是浏览器加载和渲染之后,并且在AngularJS,根据模板、控制器、模型信息修改之后的DOM。
在AngularJS对MVC的实现中,视图是知道模型和控制器的。视图知道模型的双向绑定,视图通过指令知道控制器,比如ngController
和ngView
指令,也可以通过绑定知道,比如{{someControllerFunction()}}
。通过这些方式,视图可以调用相应控制器中的方法。
加油!