AngularJS自学之路(三)——控制器和表达式

控制器

控制器在AngularJS中的作用是增强视图。

AngularJS中的控制器是一个函数,用来向视图的作用域中添加额外的功能。我们用它来给作用域对象设置初始状态,并添加自定义行为。

当我们在页面上创建一个新的控制器时,AngularJS会生成并传递一个新的$scope给这个控制器。可以在这个控制器里初始化scope。由于AngularJS会负责处理控制器的实例化过程,我们只需编写构造函数即可。下面的例子展示了控制器初始化:

function FirstController($scope) {
$scope.message = "hello";
}

细心的读者会发现,我们是在全局作用域中创建的这个函数。这样做并不合适,因为会污染全局命名空间。更合理的方式是创建一个模块,然后在模块中创建控制器,如下所示:

var app = angular.module(‘app‘, []);
app.controller(‘FirstController‘, function($scope) {
$scope.message = "hello";
});

只需创建控制器作用域中的函数,就能创建可以在视图中使用的自定义操作。很幸运,AngularJS允许我们在视图中像调用普通数据一样调用$scope上的函数。

用内置指令ng-click可以将按钮、链接等其他任何DOM元素同点击事件进行绑定。ng-click指令将浏览器中的mouseup事件,同设置在DOM元素上的事件处理程序绑定在一起(例如,当浏览器在某个DOM元素上触发了点击事件,函数就会被调用)。和前面的例子类似,绑定看起来是这样的:

<div ng-controller="FirstController">
<h4>The simplest adding machine ever</h4>
<button ng-click="add(1)" class="button">Add</button>
<a ng-click="subtract(1)" class="button alert">Subtract</a>
<h4>Current count: {{ counter }}</h4>
</div>

按钮和链接都被绑定在了内部$scope的一个操作上,当点击任何一个元素时AngularJS都会调用相应的方法。注意,当设置调用哪个函数时,会同时用括号传递一个参数(add(1))。

下面给FirstController添加一个操作:

app.controller(‘FirstController‘, function($scope) {
$scope.counter = 0;
$scope.add = function(amount) { $scope.counter += amount; };
$scope.subtract = function(amount) { $scope.counter -= amount; };
});

控制器可以将与一个独立视图相关的业务逻辑封装在一个独立的容器中。尽可能地精简控制器是很好的做法。作为AngularJS开发者,使用依赖注入来访问服务可以实现这个目的。

AngularJS同其他JavaScript框架最主要的一个区别就是,控制器并不适合用来执行DOM操作、格式化或数据操作,以及除存储数据模型之外的状态维护操作。它只是视图和$scope之间的桥梁。

控制器嵌套(作用域包含作用域)

默认情况下,AngularJS在当前作用域中无法找到某个属性时,便会在父级作用域中进行查找。如果AngularJS找不到对应的属性,会顺着父级作用域一直向上寻找,直到抵达$rootScope为止。如果在rootScope中也找不到,程序会继续运行,但视图无法更新。

通过例子来看一下这个行为。创建一个ParentController,其中包含一个user对象,再创建一个ChildController来引用这个对象:

app.controller(‘ParentController‘, function($scope) {
$scope.person = {greeted: false};
});
app.controller(‘ChildController‘, function($scope) {
$scope.sayHello = function() {
$scope.person.name = ‘Ari Lerner‘;
};
});

如果我们将ChildController置于ParentController内部,那ChildController的$scope对象的父级作用域就是ParentController的scope对象。根据原型继承的机制,我们可以在子作用域中访问ParentController的scope对象。

例如,我们可以在ChildController的DOM元素中访问定义在ParentController中的person对象

<div ng-controller="ParentController">
<div ng-controller="ChildController">
<a ng-click="sayHello()">Say hello</a>
</div>
{{ person }}
</div>

我们看到,点击按钮时,可以在ChildController中访问ParentController中$scope.person的值,就好像person对象定义在ChildController的scope中一样。

控制器应该尽可能保持短小精悍,而在控制器中进行DOM操作和数据操作则是一个不好的实践。

设计良好的应用会将复杂的逻辑放到指令和服务中。通过使用指令和服务,我们可以将控制器重构成一个轻量且更易维护的形式

简洁的控制器:

angular.module(‘myApp‘, [])
.controller(‘MyController‘, function($scope,UserSrv) {
// 内容可以被指令控制
$scope.onLogin = function(user) {
UserSrv.runLogin(user);
};
});

表达式

前面已经见过使用表达式的示例。用{{ }}符号将一个变量绑定到$scope上的写法本质上就是一个表达式:{{ expression }}。当用watch进行监听时,AngularJS会对表达式或函数进行运算。

表达式和eval(javascript)非常相似,但是由于表达式由AngularJS来处理,它们有以下显著不同的特性:

  1. 所有的表达式都在其所属的作用域内部执行,并有访问本地$scope的权限;
  2. 如果表达式发生了TypeError和ReferenceError并不会抛出异常;
  3. 不允许使用任何流程控制功能(条件控制,例如if/else);
  4. 可以接受过滤器和过滤器链。

对表达式进行的任何操作,都会在其所属的作用域内部执行,因此可以在表达式内部调用那些限制在此作用域内的变量,并进行循环、函数调用、将变量应用到数学表达式中等操作。

解析AngularJS 表达式

尽管AngularJS会在运行$digest循环的过程中自动解析表达式,但有时手动解析表达式也是非常有用的。

AngularJS通过$parse这个内部服务来进行表达式的运算,这个服务能够访问当前所处的作用域。

将$parse服务注入到控制器中,然后调用它就可以实现手动解析表达式。举例来说,如果页面上有一个输入框绑定到了expr变量上,如下所示:

<div ng-controller="MyController">
<input ng-model="expr" type="text"
placeholder="Enter an expression" />
<h2>{{ parseValue }}</h2>
</div>

我们可以在MyController中给expr这个表达式设置一个$watch并解析它:

angular.module("myApp", [])
.controller(‘MyController‘,
function($scope,$parse) {
$scope.$watch(‘expr‘, function(newVal, oldVal, scope) {
if (newVal !== oldVal) {
// 用该表达式设置parseFun
var parseFun = $parse(newVal);
// 获取经过解析后表达式的值
$scope.parsedValue = parseFun(scope);
}
});
});

插值字符串

在AngularJS中,我们的确有手动运行模板编译的能力。例如,插值允许基于作用域上的某个条件实时更新文本字符串。

要在字符串模板中做插值操作,需要在你的对象中注入$interpolate服务。在下面的例子中,我们将会将它注入到一个控制器中:

angular.module(‘myApp‘, []).controller(‘MyController‘,
function($scope, $interpolate) {
// 我们同时拥有访问$scope和$interpolate服务的权限
});

$interpolate服务是一个可以接受三个参数的函数,其中第一个参数是必需的。

  1. text(字符串):一个包含字符插值标记的字符串。
  2. mustHaveExpression(布尔型):如果将这个参数设为true,当传入的字符串中不含有表达式时会返回null。
  3. trustedContext(字符串):AngularJS会对已经进行过字符插值操作的字符串通过$sec.getTrusted()方法进行严格的上下文转义。

$interpolate服务返回一个函数,用来在特定的上下文中运算表达式。

设置好这些参数后,就可以在控制器中进行字符插值的操作了。例如,假设我们希望可以在电子邮件的正文中进行实时编辑,当文本发生变化时进行字符插值操作并将结果展示出来。

<div ng-controller="MyController">
<input ng-model="to"
type="email"
placeholder="Recipient" />
<textarea ng-model="emailBody"></textarea>
<pre>{{ previewText }}</pre>
</div>

由于控制器内部设置了一个需要每次变化都重新进行字符插值的自定义输入字段,因此需要设置一个$watch来监听数据的变化。

简而言之,$watch函数会监视scope上的某个属性。只要属性发生变化就会调用对应的函数。可以使用watch函数在scope上某个属性发生变化时直接运行一个自定义函数。

在控制器中,我们设置了$watch来监视邮件正文的变化,并将emailBody属性的值进行字符插值后的结果赋值给previewText属性。

angular.module(‘myApp‘, [])
.controller(‘MyController‘, function($scope, $interpolate) {
// 设置监听
$scope.$watch(‘emailBody‘, function(body) {
if (body) {
var template = $interpolate(body);
$scope.previewText =
template({to: $scope.to});
}
};
});
在

现在,在{{ previewText }}内部的文本中可以将{{ to }}当做一个变量来使用,并对文本的变化进行实时更新。

用startSymbol()方法可以修改标识开始的符号。这个方法接受一个参数。

用endSymbol()方法可以修改标识结束的符号。这个方法也接受一个参数。

如果要修改这两个符号的设置,需要在创建新模块时将$interpolateProvider注入进去。

angular.module(‘emailParser‘, [])
.config([‘$interpolateProvider‘, function($interpolateProvider) {
$interpolateProvider.startSymbol(‘__‘);
$interpolateProvider.endSymbol(‘__‘);
}])
.factory(‘EmailParser‘, [‘$interpolate‘, function($interpolate) {
// 处理解析的服务
return {
parse: function(text, context) {
var template = $interpolate(text);
return template(context);
}
};
}]);

现在,我们已经创建了一个模块,可以将它注入到应用中,并在邮件正文的文本中运行这个邮件解析器:

angular.module(‘myApp‘, [‘emailParser‘])
.controller(‘MyController‘, [‘$scope‘, ‘EmailParser‘,
function($scope, EmailParser) {
// 设置监听
$scope.$watch(‘emailBody‘, function(body) {
if (body) {
$scope.previewText = EmailParser.parse(body, {
to: $scope.to
});
}
});
}]);

现在用自定义的 __ 符号取代默认语法中的 {{ }} 符号来请求插值文本。

由于我们将表达式开始和结束的符号都设置成了__,因此需要将HTML修改成用这个符号取代{{ }}的版本,

<div id="emailEditor">
<input ng-model="to"
type="email"
placeholder="Recipient" />
<textarea ng-model="emailBody"></textarea>
</div>
<div id="emailPreview">
<pre>__ previewText __</pre>
</div>

时间: 2024-10-03 10:04:09

AngularJS自学之路(三)——控制器和表达式的相关文章

AngularJS自学之路(二)——模块和作用域

模块 使用模块带来的好处 1. 保持全局命名空间的清洁: 2. 编写测试代码更容易,并能保持其清洁,以便更容易找到互相隔离的功能: 3. 易于在不同应用间复用代码: 4. 使应用能够以任意顺序加载代码的各个部分. AngularJS允许我们使用angular.module()方法来声明模块,这个方法能够接受两个参数, 第一个是模块的名称,第二个是依赖列表,也就是可以被注入到模块中的对象列表. //这个方法相当于AngularJS模块的setter方法,是用来定义模块的. angular.modu

AngularJS自学之路——初识AngularJS和数据绑定

AngularJS 是什么 AngularJS的官方文档是这样介绍它的.完全使用JavaScript编写的客户端技术.同其他历史悠久的Web技术(HTML.CSS和JavaScript)配合使用,使Web应用开发比以往更简单.更快捷. AngularJS主要用于构建单页面Web应用.它通过增加开发人员和常见Web应用开发任务之间的抽象级别,使构建交互式的现代Web应用变得更加简单. AngularJS的开发团队将其描述为一种构建动态Web应用的结构化框架. AngularJS使开发Web应用变得

马士兵_JAVA自学之路(为那些目标模糊的码农们)

转载自:http://blog.csdn.net/anlidengshiwei/article/details/42264301 JAVA自学之路 一:学会选择 为了就业,不少同学参加各种各样的培训. 决心做软件的,大多数人选的是java,或是.net,也有一些选择了手机.嵌入式.游戏.3G.测试等. 那么究竟应该选择什么方向呢? 我的意见是,不要太过相信各种培训机构或是抢手文章的说法(包括我),当你要走向社会的时候,就不要再把自己当成学生,不要把自己的将来交给别人,学会运用自己的眼睛去观察,去

【转】JAVA自学之路

JAVA自学之路 一: 学会选择 为了就业,不少同学参加各种各样的培训. 决心做软件的,大多数人选的是java,或是.net,也有一些选择了手机.嵌入式.游戏.3G.测试等. 那么究竟应该选择什么方向呢? 我的意见是,不要太过相信各种培训机构或是抢手文章的说法(包括我),当你要走向社会的时候,就不要再把自己当成学生,不要把自己的将来交给别人,学会运用自己的眼睛去观察,去了解这个世界吧. 每个培训机构都会宣传自己的好处,并不能说明大的趋势. 一些新闻文章很有可能是枪手写的,不必太过相信.国外背景的

[转载] JAVA自学之路

原文链接http://blog.csdn.net/mengxin846/article/details/2219844 虽然文章有点旧但还是有些启发的,譬如要事第一,好读书不求甚解. JAVA自学之路 一:学会选择 为了就业,不少同学参加各种各样的培训. 决心做软件的,大多数人选的是java,或是.net,也有一些选择了手机.嵌入式.游戏.3G.测试等. 那么究竟应该选择什么方向呢? 我的意见是,不要太过相信各种培训机构或是抢手文章的说法(包括我),当你要走向社会的时候,就不要再把自己当 成学生

马士兵:JAVA自学之路

JAVA自学之路 一:学会选择 为了就业,不少同学参加各种各样的培训.决心做软件的,大多数人选的是java,或是.net,也有一些选择了手机.嵌入式.游戏.3G.测试等. 那么究竟应该选择什么方向呢?我的意见是,不要太过相信各种培训机构或是抢手文章的说法(包括我),当你要走向社会的时候,就不要再把自己当成学生,不要把自己的将来交给别人,学会运用自己的眼睛去观察,去了解这个世界吧. 每个培训机构都会宣传自己的好处,并不能说明大的趋势.一些新闻文章很有可能是枪手写的,不必太过相信.国外背景的教师和课

我的web前端自学之路-心得篇:我为什么要学习web前端?

时光如流水,转眼间,自己已经是大三的学长了,看着一个个学弟学妹,心中有种莫名的感觉,很怀念大学的前两年时光,但也很憧憬着自己的未来,自己将要去经历很多从未经历的事.我是我们学校信科院的一名学生,在编程方面,一开始只是接触到了C语言,但是c语言对于我来说并不友好,也并不是那么的好学,所以自己对程序不是很有兴趣,但一个偶然的机会,我接触到了web前端,看着我的一个大牛同学用前端 所涉及的语言写出了一些很棒的程序,于是就产生了一种很想学习前端的想法和很想把前端做的完美的渴望,于是,就开始了我的前端之路

.NET程序员的Android自学之路(一):Android开发环境的准备

写在前面的话:话说,笔者是一个.NET程序员,技术水平可能还不入流,这两三年一直在关注WP,可惜诺基亚"一蹶不振",没有迎来WP的时代,现在只好投入Android阵营.其实也就是今天笔者刚开始学习Android的,计划将学习的过程记录下来,一是方便自己回顾,二是分享于有需要的人,闲话少提,开始今天的内容. 一.开发环境下载Java环境JDK:Oracle官网下载地址JDK7Android环境SDK:http://developer.android.com/sdk/index.html,

Node.js自学之路——1.环境搭建

序—— <Node.js自学之路>系列文章,将记录我学习基于Node.js进行Web开发的过程. Node.js是基于V8引擎运行的开发平台,执行JavaScript速度快.性能好:也因其以JavaScript作为开发语言,对于前端工程师而言,它的学习曲线更低.开发效率更高. 一.安装Node.js 1.进入Node.js官网下载并安装 2.启动cmd输入命令查看node及npm版本 node -v npm -v npm是node完成安装后自带的包管理器 二.安装Express express