EmberJs之数组绑定@each&[]

写在前面

  好长时间没有写博客了,昨天花了些时间又整理了下之前发布过的《Ember.js之computed Property》文章,并创建了一个测试代码库,花了些时间,希望能使用测试代码的方式,来演示如何使用Ember.js同时能避免升级Ember版本后,一些功能上的变化所带来的隐含Bug。

  如果大家对Ember.js有兴趣想一起研究的话,欢迎大家一起维护测试代码库  :)

  本文主要是针对Ember.Array中的[]和@each,数组方法进行详细分析。

文章索引

JS前端框架之Ember.js系列

一、如何使用[] & @each

  Ember中二者均可用于计算属性的绑定,使用十分方便,可以像如下方式定义依赖关系:

1 totalArea: Ember.computed(‘sizeArray.[]‘, function () {
2       return this.get(‘sizeArray‘).reduce(function (prev, cur) {
3         return prev + Ember.get(cur, ‘height‘) * Ember.get(cur, ‘width‘);
4       }, 0);
5 }),

  或者:

1 totalArea: Ember.computed(‘[email protected]‘, function () {
2       return this.get(‘sizeArray‘).reduce(function (prev, cur) {
3         return prev + Ember.get(cur, ‘height‘) * Ember.get(cur, ‘width‘);
4       }, 0);
5 }),

  这样就定义了一个依赖于数组sizeArray的计算属性,建立一个绑定关系,只要sizeArray发生变化,totalArea就会被重新计算,使用十分简单,只要在function前面罗列出依赖的属性即可。虽然使用简单,但是在使用上还是有些细节的,下一节将用测试代码来讲解使用上的细节。

注:Ember计算属性有多种写法,这里给出了Ember推荐写法,更多具体细节请参考文章《Ember.js之computed Property-1. What is CP》

二、Array.[] & [email protected]特性总结

(Ember v1.13.7) 

1. [email protected] 特性

当CP属性依赖于.property(‘[email protected]‘)时:

  - columns里面任何一个元素的isLoaded属性发生变化。

  - columns增加元素或者删除子元素。

  - columns数组本身被重新赋值。

  - 不能依赖@[email protected] 。

 1 test(‘computed property depend on @each.height‘, function (assert) {
 2   var Size = Ember.Object.extend({height: 0, width: 0});
 3   var rectangle = Ember.Object.extend({
 4     totalArea: Ember.computed(‘[email protected]‘, function () {
 5       return this.get(‘sizeArray‘).reduce(function (prev, cur) {
 6         return prev + Ember.get(cur, ‘height‘) * Ember.get(cur, ‘width‘);
 7       }, 0);
 8     }),
 9     sizeArray: [
10       Size.create({height: 10, width: 10}),
11       Size.create({height: 10, width: 10})
12     ]
13   }).create();
14   var sizeArray = rectangle.get(‘sizeArray‘);
15
16   assert.equal(rectangle.get(‘totalArea‘), 200, "the total area should be 200");
17
18   sizeArray.pushObject(Size.create({height: 10, width: 10}));
19   assert.equal(rectangle.get(‘totalArea‘), 300, "the total area should be 300 after added");
20
21   sizeArray.removeAt(0);
22   assert.equal(rectangle.get(‘totalArea‘), 200, "the total area should be 200 after removed");
23
24   sizeArray[0].set(‘height‘, 20);
25   assert.equal(rectangle.get(‘totalArea‘), 300, "the total area should be 300 after ‘height‘ changed");
26
27   sizeArray[0].set(‘width‘, 20);
28   assert.equal(rectangle.get(‘totalArea‘), 300, "the total area should not be changed after ‘width‘ changed");
29
30   sizeArray.clear();
31   assert.equal(rectangle.get(‘totalArea‘), 0, "the total area should be 0 after reset rectangle.sizeArray");
32 });

[参考代码](https://github.com/Cuiyansong/ember-table-learnning/blob/master/ember-test/tests/unit/computed-property-test.js#L164-Lundefined)

2. [email protected]特性

当CP属性依赖于.property(‘[email protected]‘)时:

  - columns增加或删除元素。

  - columns自身被替换或重新赋值。

 1 test(‘computed property depend on @each‘, function (assert) {
 2   var Size = Ember.Object.extend({height: 0, width: 0});
 3   var rectangle = Ember.Object.extend({
 4     totalArea: Ember.computed(‘[email protected]‘, function () {
 5       return this.get(‘sizeArray‘).reduce(function (prev, cur) {
 6         return prev + Ember.get(cur, ‘height‘) * Ember.get(cur, ‘width‘);
 7       }, 0);
 8     }),
 9     sizeArray: [
10       Size.create({height: 10, width: 10}),
11       Size.create({height: 10, width: 10})
12     ]
13   }).create();
14   var sizeArray = rectangle.get(‘sizeArray‘);
15
16   assert.equal(rectangle.get(‘totalArea‘), 200, "the total area should be 200");
17
18   sizeArray.pushObject(Size.create({height: 10, width: 10}));
19   assert.equal(rectangle.get(‘totalArea‘), 300, "the total area should be 300 after added");
20
21   sizeArray.removeAt(0);
22   assert.equal(rectangle.get(‘totalArea‘), 200, "the total area should be 200 after removed");
23
24   sizeArray[0].set(‘height‘, 20);
25   assert.equal(rectangle.get(‘totalArea‘), 200, "the total area should not be changed");
26
27   sizeArray.clear();
28   assert.equal(rectangle.get(‘totalArea‘), 0, "the total area should be 0 after reset rectangle.sizeArray");
29 });

[参考代码](https://github.com/Cuiyansong/ember-table-learnning/blob/master/ember-test/tests/unit/computed-property-test.js#L104)

3. Array.[]特性

当CP属性依赖于.property(‘columns.[]‘)时:

  - 与绑定.property(‘[email protected]‘) 行为相同。

 1 test(‘computed property depend on []‘, function (assert) {
 2   var Size = Ember.Object.extend({height: 0, width: 0});
 3   var rectangle = Ember.Object.extend({
 4     totalArea: Ember.computed(‘sizeArray.[]‘, function () {
 5       return this.get(‘sizeArray‘).reduce(function (prev, cur) {
 6         return prev + Ember.get(cur, ‘height‘) * Ember.get(cur, ‘width‘);
 7       }, 0);
 8     }),
 9     sizeArray: [
10       Size.create({height: 10, width: 10}),
11       Size.create({height: 10, width: 10})
12     ]
13   }).create();
14   var sizeArray = rectangle.get(‘sizeArray‘);
15
16   assert.equal(rectangle.get(‘totalArea‘), 200, "the total area should be 200");
17
18   sizeArray.pushObject(Size.create({height: 10, width: 10}));
19   assert.equal(rectangle.get(‘totalArea‘), 300, "the total area should be 300 after added");
20
21   sizeArray.removeAt(0);
22   assert.equal(rectangle.get(‘totalArea‘), 200, "the total area should be 200 after removed");
23
24   sizeArray[0].set(‘height‘, 20);
25   assert.equal(rectangle.get(‘totalArea‘), 200, "the total area should not be changed");
26
27   sizeArray.clear();
28   assert.equal(rectangle.get(‘totalArea‘), 0, "the total area should be 0 after reset rectangle.sizeArray");
29 });

[参考代码](https://github.com/Cuiyansong/ember-table-learnning/blob/master/ember-test/tests/unit/computed-property-test.js#L134)

(Ember v2.0.0)

2015-08-17 除建议用[]代替@each,暂未发现其他变化。

[参考代码](https://github.com/emberjs/ember.js/blob/v2.0.0/packages/ember-metal/lib/computed.js#L224)

注: 更多关于计算属性问题,请参考文章《Ember.js之computed Property-3.CP重要原则》

三、源码分析Array.[] & [email protected]

[email protected]返回的是一个特殊类型,这个类型便于实现绑定。

 1   ‘@each‘: computed(function() {
 2     if (!this.__each) {
 3       // ES6TODO: GRRRRR
 4       var EachProxy = requireModule(‘ember-runtime/system/each_proxy‘)[‘EachProxy‘];
 5
 6       this.__each = new EachProxy(this);
 7     }
 8
 9     return this.__each;
10   })

[参考这里](https://github.com/emberjs/ember.js/blob/stable-1-13/packages/ember-runtime/lib/mixins/array.js#L516)

Array.[]继承自Ember.Enumerable.[]并且返回this。

1   ‘[]‘: computed({
2     get(key) {
3       return this;
4     },
5     set(key, value) {
6       this.replace(0, get(this, ‘length‘), value);
7       return this;
8     }
9   }),

[参考这里](https://github.com/emberjs/ember.js/blob/stable-1-13/packages/ember-runtime/lib/mixins/array.js#L164)

二者之间唯一的区别是Array.[]返回的是一对象自身(普通对象组成的数组), 而[email protected]返回的是EachProxy对象, 针对普通对象组成的数组而言,仅仅能检测到数组长度的变化和对象本身的变化,对数组内容发生变化则不得而知,而EachProxy对每一个元素增加observerListener监听器,当数组内容发生变化时,通知数组发生变更,实现了数组元素这一级别的监听。

 1 var EachProxy = EmberObject.extend({
 2
 3   init(content) {
 4     this._super(...arguments);
 5     this._content = content;
 6     content.addArrayObserver(this);
 7
 8     // in case someone is already observing some keys make sure they are
 9     // added
10     forEach(watchedEvents(this), function(eventName) {
11       this.didAddListener(eventName);
12     }, this);
13   },
14
15 ...

[参考这里](https://github.com/emberjs/ember.js/blob/stable-1-13/packages/ember-runtime/lib/system/each_proxy.js#L109)

四、相关引用

[[email protected]](http://emberjs.com/api/classes/Ember.Array.html#property__each)

[Emer-EachProxy](https://github.com/emberjs/ember.js/blob/stable-1-13/packages/ember-runtime/lib/system/each_proxy.js)

[Ember-study](https://github.com/Cuiyansong/ember-table-learnning)

时间: 2024-12-13 20:06:22

EmberJs之数组绑定@each&[]的相关文章

给js创建的一个input数组绑定click事件

</pre><pre name="code" class="javascript"><html> <body> <input type="button" name="input[]" value="按钮1" /><br /> <input type="button" name="input[]&quo

asp.net mvc int[] 和 string[] 自定义数组绑定

新建类,int[]数组模型绑定 using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace Koukou.Admin.ModelBinder { public class IntArrayModelBinder : DefaultModelBinder { public override object BindModel(C

Spring MVC数组绑定

需求:商品批量删除,用户在页面选择多个商品,批量删除. 关键:将页面选择(多选)的商品id,传到controller方法的形参,方法形参使用数组接收页面请求的多个商品id // 批量删除 商品信息 @RequestMapping("/deleteItems") public String deleteItems(Integer[] items_id) throws Exception { // 调用service批量删除商品 // ... return "success&qu

springMVC学习(6)-包装pojo类型、数组、list、Map类型参数绑定

一.包装类型pojo参数绑定: 需求:商品查询controller方法中实现商品查询条件传入. 实现方法: 1)在形参中 添加HttpServletRequest request参数,通过request接收查询条件参数. 2)在形参中让包装类型的pojo接收查询条件参数. 做法:参数名和包装pojo中的属性一致即可: (本例中:<input name="itemsCustom.name" />传递参数  和  ItemsQueryVo属性名itemsCustom一致); 二

[WPF]绑定到界面的数组不支持调度线程以外对其更改的办法

[原]WPF编程经常遇到一个问题: 某个数组己绑定到主界面某控件中,然后在后台程序中需要对数组增(减)数据,然后程序就会报错, 程序提示:该类型的CollectionView 不支持从调度程序线程以外的线程对其SourceCollection进行的更改. 如下图所示: 既然不能这样操作,就得想一个办法来解决,现在先把把出现错误的程序全部列出来,然后再来根据解决办法进行修改, 本测试程序先建一个学生类: using System; using System.Collections.Generic;

MVC数组模型绑定

ASP.NET MVC数组模型绑定 在ASP.NET MVC中使用Razor语法可以在视图中方便地展示数组,如果要进行数组模型绑定,会遇到索引断裂问题,如下示例: <input type="text" name="[0].Name" /> <input type="text" name="[1].Name" /> <input type="text" name="[2

ASP.NET MVC数组模型绑定

在ASP.NET MVC中使用Razor语法可以在视图中方便地展示数组,如果要进行数组模型绑定,会遇到索引断裂问题,如下示例: <input type="text" name="[0].Name" /> <input type="text" name="[1].Name" /> <input type="text" name="[2].Name" />

.NET Core采用的全新配置系统[4]: &ldquo;Options模式&rdquo;下各种类型的Options对象是如何绑定的?

旨在生成Options对象的配置绑定实现在IConfiguration接口的扩展方法Bind上.配置绑定的目标类型可以是一个简单的基元类型,也可以是一个自定义数据类型,还可以是一个数组.集合或者字典类型.通过前面的介绍我们知道ConfigurationProvider将原始的配置数据读取出来后会将其转成Key和Value均为字符串的数据字典,那么针对这些完全不同的目标类型,原始的配置数据如何通过数据字典的形式来体现呢? [ 本文已经同步到<ASP.NET Core框架揭秘>之中] 目录一.绑定

SpringMVC学习10-参数绑定高级篇

1.包装类型pojo参数绑定 需求 商品查询controller方法中实现商品查询条件传入. 实现方法 第一种方法:在形参添加HttpServletRequest request参数,通过request接收查询条件参数. 第二种方法:在形参中让包装类型的pojo接收查询条件参数. 分析: 页面传参数的特点:复杂,多样性.条件包括 :用户账号.商品编号.订单信息... 如果将用户账号.商品编号.订单信息等放在简单pojo(属性是简单类型)中,pojo类属性比较多,比较乱. 建议使用包装类型的poj