AngularJS权威教程 第6章 表达式expression
表达式在AngularJS应用中被广泛使用,因此深入理解AngularJS如何使用并运算表达式是非常重要的。
前面已经见过使用表达式的示例。用{{ }}符号将一个变量绑定到$scope上的写法本质上就是一个表达式:{{ expression }}。当用$watch进行监听时,AngularJS会对表达式或函数进行运算。
表达式和eval(javascript)非常相似,但是由于表达式由AngularJS来处理,它们有以下显著不同的特性:
- 所有的表达式都在其所属的作用域内部执行,并有访问本地$scope的权限;
- 如果表达式发生了TypeError和ReferenceError并不会抛出异常;
- 不允许使用任何流程控制功能(条件控制,例如if/eles);
- 可以接受过滤器和过滤器链。
对表达式进行的任何操作,都会在其所属的作用域内部执行,因此可以在表达式内部调用那些限制在此作用域内的变量,并进行
循环、函数调用、将变量应用到数学表达式中等操作。
6.1 解析AngularJS 表达式
尽管AngularJS会在运行$digest循环的过程中自动解析表达式,但有时手动解析表达式也是非常有用的。
AngularJS通过$parse这个内部服务来进行表达式的运算,这个服务能够访问当前所处的作用域。这个过程允许我们访问定义在
$scope上的原始JavaScript数据和函数。将$parse服务注入到控制器中,然后调用它就可以实现手动解析表达式。
案例如下:
<!doctype html>
<html ng-app="myApp">
<head>
<title>Parse Expression Example</title>
<script src="http://apps.bdimg.com/libs/angular.js/1.3.9/angular.min.js"></script>
</head>
<body>
<div ng-controller="MyController">
<input ng-model="expr"
type="text"
placeholder="Enter an expression" />
<div>{{ parsedExpr }}</div>
</div>
<script>
// open this example and type person.name into the test field
angular.module('myApp', [])
.controller('MyController',
['$scope', '$parse', function($scope, $parse) {
$scope.person = {
name: "Ari Lerner"
};
$scope.$watch('expr', function(newVal, oldVal, scope) {
if (newVal !== oldVal) {
// Let's set up our parseFun with the expression
var parseFun = $parse(newVal);
// Get the value of the parsed expression, set it on the scope for output
scope.parsedExpr = parseFun(scope);
}
});
}]);
</script>
</body>
</html>
效果如图:
6.2 插值字符串
在AngularJS中,我们的确有手动运行模板编译的能力。例如,插值允许基于作用域上的某个条件实时更新文本字符串。
要在字符串模板中做插值操作,需要在你的对象中注入$interpolate服务。
$interpolate服务是一个可以接受三个参数的函数,其中第一个参数是必需的。
- text(字符串):一个包含字符插值标记的字符串。
- mustHaveExpression(布尔型):如果将这个参数设为true,当传入的字符串中不含有表达式时会返回null。
- trustedContext(字符串):AngularJS会对已经进行过字符插值操作的字符串通过
- $sec.getTrusted()方法进行严格的上下文转义。
$interpolate服务返回一个函数,用来在特定的上下文中运算表达式。设置好这些参数后,就可以在控制器中进行字符插值的操
作了。
由于控制器内部设置了一个需要每次变化都重新进行字符插值的自定义输入字段,因此需要设置一个$watch来监听数据的变化。
简而言之,$watch函数会监视$scope上的某个属性。只要属性发生变化就会调用对应的函数。可以使用$watch函数在$scope上某
个属性发生变化时直接运行一个自定义函数。
案例如下:
<!doctype html>
<html ng-app="myApp">
<head>
<title>Interpolate String Template Example</title>
<script src="http://apps.bdimg.com/libs/angular.js/1.3.9/angular.min.js"></script>
</head>
<body>
<div id="emailEditor" ng-controller="MyController">
To:<input ng-model="to"
type="email"
placeholder="Recipient" />
EmailBody:<textarea ng-model="emailBody"></textarea>
<div id="emailPreview">
<pre>__ previewText __</pre>
</div>
</div>
<script>
// to see this example in action open in your browser, type
// an email address into the email field. Then in the text area
// box type: Hello {{ to }}.
angular.module('myApp', ['emailParser'])
.controller('MyController',
['$scope', 'EmailParser',
function($scope, EmailParser) {
// Set up a watch
$scope.$watch('emailBody', function(body) {
if (body) {
$scope.previewText =
EmailParser.parse(body, {
to: $scope.to
});
}
});
}]);
angular.module('emailParser', [])
.config(['$interpolateProvider',
function($interpolateProvider) {
$interpolateProvider.startSymbol('__');
$interpolateProvider.endSymbol('__');
}])
.factory('EmailParser', ['$interpolate',
function($interpolate) {
// a service to handle parsing
return {
parse: function(text, context) {
var template = $interpolate(text);
return template(context);
}
};
}]);
</script>
</body>
</html>
案例解析:
在控制器中,我们设置了$watch来监视邮件正文的变化,并将emailBody属性的值进行字符插值后的结果赋值给previewText属性。
现在,在{{ previewText }}内部的文本中可以将{{ to }}当做一个变量来使用,并对文本的变化进行实时更新。(如果需要在文本中
使用不同于{{ }}的符号来标识表达式的开始和结束,可以在$inter polateProvider中配置。)
用startSymbol()方法可以修改标识开始的符号。这个方法接受一个参数。
- value(字符型):开始符号的值。
用endSymbol()方法可以修改标识结束的符号。这个方法也接受一个参数。
- value(字符型): 结束符号的值。
如果要修改这两个符号的设置,需要在创建新模块时将$interpolateProvider注入进去。现在用自定义的 __ 符号取代默认语法中
的 {{ }} 符号来请求插值文本。
效果如图: