博弈AngularJS讲义(9) - 表达式
Angular表达式
Angular表达式类似于JS代码,有双大括号绑定,其格式为{{expression}}.
例如:
1+2 - 两个常量数字相加即 3
a+b - 作用域中的两个model相加
user.name - 获取作用域中属性user的name属性
items[index] - 获取作用域中items数组的元素
与JavaScript表达式的区别
Angular看似JavaScript表达式,但是有如下一些区别:
1. 从上下文看, JS表达式实在全局变量window的上下文中运行,而Angular表达式这在相应的作用域中(scope)工作。
2. 从容错性上来说,在JS中如果对undefined或者null类型操作会产生ReferenceError或者TypeError之类的错误。Angular表达式则不会报错。
3. 相比于JS, Angular表达式不支持控制流结构语句, 例如条件循环语句、异常等。
4. Angular提供了过滤器机制在数据展现之前对其格式化。
如果需要在视图中调用复杂的JS代码,可以将其写在控制器方法中,或者用Angular的$eval方法。
让我们通过具体例子来理Angular解表达式:
例一:
<span> 1+2={{1+2}} </span>
运行结果:
例二:
HTML代码
<div ng-controller="ExampleController" class="expressions"> Expression: <input type='text' ng-model="expr" size="80"/> <button ng-click="addExp(expr)">Evaluate</button> <ul> <li ng-repeat="expr in exprs track by $index"> [ <a href="" ng-click="removeExp($index)">X</a> ] <tt>{{expr}}</tt> => <span ng-bind="$parent.$eval(expr)"></span> </li> </ul> </div>
JS代码
angular.module('expressionExample', []) .controller('ExampleController', ['$scope', function($scope) { var exprs = $scope.exprs = []; $scope.expr = '3*10|currency'; $scope.addExp = function(expr) { exprs.push(expr); }; $scope.removeExp = function(index) { exprs.splice(index, 1); }; }]);
运行结果:
1. 在控制器中我们给作用域定义了expr模型,并定义了个方法: addExp和removeExp.
2. 通过ng-click为"Evaluate"按钮绑定了addExp回调,默认expr为"3*10|currency".
3. 通过ng-repeat,迭代展现定义的表达式“<tt>{{expr}}</tt> => <span ng-bind="$parent.$eval(expr)"></span>”, 注意在ng-bind中,由于ng-repeat会创建自己的作用域,需要通过$parent.$eval来执行表达式输出货币格式$30.00.
表达式上下文
Angular没用使用JS的eval()来执行表达式, 而是通过内建的$parse服务来处理表达式。
Angular表达式不能访问全局的JS变量(例如window, document或者location). 这种限制是有意义的,它预防了因误操作全局状态而导致的错误发生。
Angular提供了$window和$location服务给Angular表达式间接调用window和localtion中的方法。
例如:
<div class="example2" ng-controller="ExampleController"> Name: <input ng-model="name" type="text"/> <button ng-click="greet()">Greet</button> <button ng-click="window.alert('Should not see me')">Won't greet</button> </div>
angular.module('expressionExample', []) .controller('ExampleController', ['$window', '$scope', function($window, $scope) { $scope.name = 'World'; $scope.greet = function() { $window.alert('Hello ' + $scope.name); }; }]);
在HTML模板中我们通过ng-click为"Greet"和"Won't greet"按钮分别绑定了回调$scope.greet()和window.alert('...'), 由于Angular上下文中不能访问window等全局变量,所以"Won't greet"将不会弹出alert框,而greet按钮通过$window.alert可弹出对话框。
容错机制
Angular表达式对于undefined和null类型使用时不会报错,而是自动判断停止后续调用。 在JS中执行a.b.c会在a不是对象的情况下抛出异常,这在通用编程语言中是普遍的正确做法。由于Angular表达式主要用于在视图中展示,出于不同的目的,在遇到对undefined或者null类型的方法调用或获取属性时,Angular表达式会直接返回undefined,不展示任何内容,这也简化了代码,否则表达式会很复杂{{((a||{}).b||{}).c}}.
没有控制结构
除了三元运算(a ? b : c), Angular不支持其他控制结构。Angular的哲学是把复杂的应用逻辑封装在控制器中而不是视图中。
不支持正则表达式
为了尽量保持表达式简洁,Angular表达式不支持正则,如果需要实现复杂的模型转换,可以将这部分逻辑是现在控制器中或者过滤器中。
$event
事件指令如ng-click、ng-focus会在指令的作用域中将$event对象暴露给表达式,即可通过表达式访问$event对象,这是一个jQuery事件类型的对象。
让我们看具体的代码示例:
<div ng-controller="EventController"> <button ng-click="clickMe($event)">Event</button> <p><code>$event</code>: <pre> {{$event | json}}</pre></p> <p><code>clickEvent</code>: <pre>{{clickEvent | json}}</pre></p> </div>
angular.module('eventExampleApp', []). controller('EventController', ['$scope', function($scope) { /* * expose the event object to the scope */ $scope.clickMe = function(clickEvent) { $scope.clickEvent = simpleKeys(clickEvent); console.log(clickEvent); }; /* * return a copy of an object with only non-object keys * we need this to avoid circular references */ function simpleKeys (original) { return Object.keys(original).reduce(function (obj, key) { obj[key] = typeof original[key] === 'object' ? '{ ... }' : original[key]; return obj; }, {}); } }]);
运行结果:
注意. 在该实例中我们在ng-click和表达式中都传入了$event,而只有ng-click可访问到$event事件,通过{{clickEvent|json}}展现, 是因为$event在ng-click的作用域中才可见,对于表达式{{$event|json}}不可见。
一次绑定
如果表达式含有::前缀则表明这个表达式对模型是一次性绑定的。 一次性绑定意味着表达式只会计算一次,而不会因后续模型数据的变化而重新计算。
让我们看如下实例:
<div ng-controller="EventController"> <button ng-click="clickMe($event)">Click Me</button> <p id="one-time-binding-example">One time binding: {{::name}}</p> <p id="normal-binding-example">Normal binding: {{name}}</p> </div>
angular.module('oneTimeBidingExampleApp', []). controller('EventController', ['$scope', function($scope) { var counter = 0; var names = ['Igor', 'Misko', 'Chirayu', 'Lucas']; /* * expose the event object to the scope */ $scope.clickMe = function(clickEvent) { $scope.name = names[counter % names.length]; counter++; }; }]);
运行结果:
可以看到第一次点击往后,表达式结果都为'Igor',第二点击后由于第一个表达式是一次性绑定所以不会在赋值。一次性绑定在表达式执行一次后就接触绑定而不用循环监听,减少了资源消耗。
对于视图中不变的常量模型,我们应优先考虑一次性绑定, 例如文本及标签,ng-repeat常量列表,指令中的不变参数等:
<div name="attr: {{::color}}">text: {{::name}}</div> <ul> <li ng-repeat="item in ::items">{{item.name}};</li> </ul>
someModule.directive('someDirective', function() { return { scope: { name: '=', color: '@' }, template: '{{name}}: {{color}}' }; });
<div some-directive name="::myName" color="My color is {{::myColor}}"></div>
上一篇: 立夏节气养生六要点 谨防外感早睡早起
下一篇: Spring 4.0 之框架综述