0. 目录
- 目录
- 前言
- 正文
- 1 ng-controller
- 2 构建模型-Constructing the model
- 3 控制器作为属性名-Controller as propertyName
- 4 依赖注入-Dependency injection
- 41 scope
- 5 模型视图控制器MVC-Model-view-controller
- 6 函数-Functions
- 7 回调函数
- 8 结论
- 声明
1. 前言
AngularJS是为了克服HTML在构建应用上的不足而设计的。HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了,所以AngularJS做了一些工作来来解决静态网页技术在构建动态应用上的不足。
AngularJS的初衷是为了简化web APP开发,精髓是简单。但是国内外有很多AngularJS的教程,都着重讲AngularJS的强大之处,从而增加了AngularJS学习的难度,本教程试图用通俗的语言讲解最为基础、最为实用的内容,简化学习进程、降低学习难度是本教程的初衷。
本系列教程以翻译Chris Smith的Angualr Basics为梗概,融合博主自己的理解,为大家提供一个简单明了的学习教程。
本文为系列教程第4篇控制器,翻译自Controllers。
- 序
- 引言-Introduction
- 基础-Basics
- 控制器-Controllers
- 作用域-Scopes
- 集合-Collections
- 模块-Modules
- 依赖注入-Dependency Injection
- 服务-Services
- 过滤器-Filters
- 指令-Directives
- 指令作用域-Directive Scopes
- 路由-Routing
- 通信-HTTP
- 结论
2.正文
在前一章中,我让您勒住缰绳,只把Angular作为HTML的功能扩展,当然Angular远不止这些,事实上通过Javascript实现自定义行为是每个Angular项目的必要部分。如果您在前一章中抱有编程的冲动,你的耐心应该得到嘉奖,现在该写点Javascript了。
最常见的给Angular view(视图)增加控制的方法是通过控制器(controller),最简单的书写控制器的方法是通过书写构造函数的方式。为了帮助大家理解整个过程,我们通过一个非常简单的控制器来演示一下(非常简单,甚至连hello world也没有,就是个空的controller)。
下面是我们的控制器(controller)。
/* empty-controller.js */
function EmptyController() {
};
当然,我们必须把angular类库导入进来,同时利用ng-app
命令告诉html,我们将在这里使用angular。
<!-- index.html -->
<!-- 导入类库 -->
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.js"></script>
<!--google被墙,大家可以用国内的cdn-->
<script src="//apps.bdimg.com/libs/angular.js/1.2.16/angular.min.js"></script>
<!-- ng-app告诉html,我们将使用angular -->
<body ng-app="app">
<!-- Other examples to be inserted here. -->
</body>
控制器可以在全局范围内使用构造函数的方式进行定义,本章就是使用这种方式。在Angular1.3以前版本中可以直接使用简单方式,不过现在需要进行一些配置,创建一个命名的应用模块(named application module)。如何使用Angular模块我们会在接下来的模块、依赖注入和服务章节中详细讲解,现在,你只需要使用下面的案例作为样板文件(boilerplate)即可。
/* module.js */
angular.module(‘app‘, []);
angular.module(‘app‘).config([‘$controllerProvider‘, function($controllerProvider) {
$controllerProvider.allowGlobals();
}]);
现在我们先不管这些,我们来试试什么都不干的控制器(noop controller)。
2.1 ng-controller
像通常那样,使用指令(directive)可以搞定这些,使用ng-controller
指令可以通过它的名字找到并调用控制器函数。
<!-- empty-controller.html -->
<p ng-controller="EmptyController">
</p>
当然,正如您所想,它将调用我们上面定义的那个EmptyController
构造函数,nothing to do。接下来,我们来研究下什么是控制器?如何使用?
2.2 构建模型-Constructing the model
在官方指南的核心概览部分把Angular的工作描述为“将变量和函数功能传递给表达式和指令”,这里的函数功能指回调函数,我们稍候重点讲解,现在先来了解一下变量初始化和模型准备。
明白Angular模型就是表达式作用域可以读取的一些普通Javascript代码之后,准备模型就是小意思了。普通javascript代码?是的,我们可以书写普通js代码,让我们给空的控制器增加一个string属性。
/* message-controller-as.js */
function MessageController() {
this.message = "This is a model.";
}
简单吧!成功了吗?我们已经成功的创建了一个模型?是也不是?
答案是几乎。当我们在试图作用域中获得message属性之后,它就是一个模型了。完成这个任务的一种方法,就是让整个控制器作为scope中的一个属性,听起来很玄乎吧,当你了解了语法的秘密,一切将变得非常简单。
2.3 控制器作为属性名-Controller as propertyName
正像官方API描述的那样,ng-controller
可以接受controll as propertyName
表达式作为参数,注意只有Angular1.2以后版本才可使用本特性。
<!-- message-controller.html -->
<p ng-controller="MessageController as controller">
{{controller.message}}
</p>
编译结果为:This is a model.
这样,我们就是用controller准备好了模型数据。
尽管这种给引用附加属性的方式直接明了,但是“把整个controller暴露给视图”容易给人误解,让人感觉控制器是模型的一部分,而实际上,控制器的作用仅仅是为模型准备数据。当你仅仅满足客户端需求,不考虑更多需求时,测试和调试都会非常简单。而且,这种方式会为视图代码带来噪声,因为所有的属性必须通过控制器的引用访问。视图代码通常是一个web应用噪声要求最苛刻的区域,它可能是一瞥之后出现理解困难的第一个地方。最后,控制器(controller)和作用域(scope)并进工作,从教学角度来说,明晰scope的引用并精细控制它,会非常有用,通过这些控制,我们可以明确地暴露我们想要暴露给视图的部分。因此,本书更倾向于这种能够明确管理scope对象的控制器样式。
那么问题来了,我们如何获得这个scope对象的引用?使用new运算符传建一个?还是从某个地方请求?
2.4 依赖注入-Dependency injection
不是的。我们通过依赖注入获得它。你可能已经注意到我并没有展示实例化控制器的代码,例如var controller=new MessageController();
那么,谁创建了视图中使用的控制器?
当然是Angular。Angular是一个控制反转(Inversion of Control,英文缩写为IoC)容器,可以管理应用组件的生存周期。当需要一个新的控制器或者新的组件时,Angular会创建一个。这可以节省时间,而且更重要的是,可以让Angular给组件注入资源、依赖性等。
2.4.1 $scope
作为一个通过依赖注入框架管理的组件,控制器只需要通过一个参数$scope即可。参数的名字非常关键,实际上,如果你压缩了你的Javascript源代码,你会破坏依赖注入机制(无须担心,有一个变通方案,我们会在依赖注入章节详解)。
/* message-controller-scope.js */
function MessageController($scope) {
$scope.message = "This is a model.";
}
通过给构造函数增加$scope
参数,我们通知Angualr我们需要一个视图作用域的引用。可以再简单一些吗?现在我们可以不在控制器里声明message
属性,而是直接在scope
中定义它。
在模板中,我们从ng-controller
指令中移除as controller
。controller.message
变成message
,这个是我们附加到scope的唯一的属性。
<!-- message-controller-scope.html -->
<p ng-controller="MessageController">
{{message}}
</p>
编译结果为:This is a model.
尽管两种方式都可以正常工作,本书更多使用指定明确的$scope
的方式。依赖注入是Angualr的特色,所以我们要尽可能的熟悉它。
2.5 模型视图控制器MVC-Model-view-controller
官方指南的控制器定义中也涉及到向视图暴露”功能性”。在我们讨论这个问题之前,我觉得有必要花点时间讨论下Angular控制器与经典MVC模式的不同。
根据维基百科model-view-controller (MVC)的解释,控制器“mediates input, converting it to commands for the model or view”。但是很难在Angular范围内理解“commands for the model or view”,因为Angular的MVC模型非常简单,采用仅有规则没有逻辑的贫血模型。截至目前,Angular最流行的方法是,把所有的业务逻辑(域逻辑)放到控制器里,换句话说,Angular应用趋向于瘦模型和胖控制器( skinny models and fat controllers)。
如果您熟悉模式,例如富域模型(rich domain model)、瘦模型胖控制器(Skinny Controller, Fat Model)等,我想你会发现Angular的方法有点倒退。我想这个只可能是优先级问题,在客户端编程中,两者之间存在着显著差异,一边是声明式的、DOM为中心的视图代码,另一边是命令式的、数据驱动的JS代码来处理业务逻辑和应用程序基础。这是巨大的成功,我很高兴类似于Angular之类的框架关注这些。一段时间之后,业务逻辑从控制器中分离开来可能占有更高的优先级,我们可能看到富模型(richer models)的趋势。
2.6 函数-Functions
让我们书写一个包含简单函数的控制器,体验下在视图中调用该函数。你可能会想起,在上一章中我们说过,Angular不允许在视图中声明函数。
下面的控制器把一个函数指定给scope的一个属性。
/* count-controller.js */
function CountController($scope) {
$scope.count = function() { return 12; }
}
表达式里调用函数方法和普通js代码没有区别,使用{{ }}
。
<!--count-controller.html-->
<p ng-controller="CountController">
There are {{count()}} months in a year.
</p>
编译结果为: There are 12 months in a year.
注意,函数不是简单地调用和忘记,而是绑定在模型上。 什么意思? 你Angular的绑定特性(你可能会逐步举得理所当然),意味着Angular不但会在视图第一次渲染时调用该函数,而且会在相关模型改变时调用。为了让大家明白工作原理,我们需要一个使用模型数据的函数,下面的求和函数使用两个scope中声明的模型属性,operand1
和operand2
。Angular将会在任意属性发生改变时调用函数和渲染结果。
/* addition-controller.js */
function AdditionController($scope) {
$scope.operand1 = 0;
$scope.operand2 = 0;
$scope.add = function() {
return $scope.operand1 + $scope.operand2;
}
$scope.options = [0,1,2,3,4];
}
因为我们已经多次使用input指令,这次让我们用Angular的select指令改变模型数据。您可能已经注意到,上面代码的最后一行的$scope.options
,我们接下来使用ng-options
指令把数组数据放置到select里面。x for x in options
这个写法感觉有点废话,无论如何记住它吧()。
<!-- addition-controller.html -->
<p ng-controller="AdditionController">
<select ng-model="operand1" ng-options="x for x in options"></select>
+ <select ng-model="operand2" ng-options="x for x in options"></select>
= {{add()}}
</p>
编译之后结果如下面动图所示。
不错吧!但是,如果你不想使用operand1和operand2而想使用属性,我们可以在泛化性上做些改善。摆脱您的重构手册、撸起袖子进行一些参数抽象。
/* addition-controller-params.js */
function AdditionController($scope) {
$scope.number = 2;
$scope.add = function(operand1, operand2) {
return operand1 + operand2;
}
}
在表达式内部,函数参数可以是属性,也可以是字面值。
<!-- addition-controller-params.html -->
<p ng-controller="AdditionController">
{{add(number, 2)}} is not the same as {{add(number, "2")}}
<br>
2 + 2 + 2 + 2 = {{add(2, add(2, add(2, 2)))}}
</p>
编译结果为:
4 is not the same as 22
2 + 2 + 2 + 2 = 8
接下来,我们通过一个回调函数来处理用户行为。
2.7 回调函数
上一章曾经有一个案例,利用ng-click切换boolean值,在ng-init
指令中初始化authorized,利用ng-click="authorized = !authorized
实现简单、行内回调,让我们把该案例做个改进,把模型初始化、切换logic变量都放到控制器里去。
/* auth-controller.js */
function AuthController($scope) {
$scope.authorized = true;
$scope.toggle = function() {
$scope.authorized = !$scope.authorized
};
}
现在toggle
是一个作用域中的函数,ng-click
的参数看起来像个函数调用toggle()
。 它不是真正的函数调用, 只是一个用户单击时会执行的字符串而已。
<!-- auth-controller.html -->
<div ng-controller="AuthController">
<p>
The secret code is
<span ng-show="authorized">0123</span>
<span ng-hide="authorized">not for you to see</span>
</p>
<input class="btn btn-xs btn-default" type="button" value="toggle" ng-click="toggle()">
</div>
编译结果如下动图所示。
案例依然正常工作,而且在一个更好的地方实现了逻辑切换。为什么更好呢?一个很好的问题。在控制器内处理用户行为的好处在于,当我们实现更为复杂的用户行为时可以保证代码清晰可读,例如处理来自远程服务器的异步模型数据(如果您迫不及待的想了解这部分知识,请跳至本书的http部分)。
2.8 结论
我们在Angular的使用中引入了javascript,控制器(controller)在MVC模式中扮演者为视图准备数据的功能,它通过在scope对象上声明属性,实现该功能。下一章中,我们将详细介绍scope对象,了解它如何组织成一个体系与应用的DOM结构匹配。
3.声明
前端开发whqet,关注前端开发,分享相关资源。csdn专家博客,王海庆希望能对您有所帮助,限于作者水平有限,出错难免,欢迎拍砖!
欢迎任何形式的转载,烦请注明装载,保留本段文字。
本文原文链接,http://blog.csdn.net/whqet/article/details/44698845
欢迎大家访问独立博客http://whqet.github.io