写在前面

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

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

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

文章索引

JS前端框架之Ember.js系列

一、如何使用[] & @each

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

 totalArea: Ember.computed('sizeArray.[]', function () {
return this.get('sizeArray').reduce(function (prev, cur) {
return prev + Ember.get(cur, 'height') * Ember.get(cur, 'width');
}, 0);
}),

  或者:

 totalArea: Ember.computed(‘sizeArray.@each', function () {
return this.get('sizeArray').reduce(function (prev, cur) {
return prev + Ember.get(cur, 'height') * Ember.get(cur, 'width');
}, 0);
}),

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

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

二、Array.[] & Array.@each特性总结

(Ember v1.13.7) 

1. Array.@each.property 特性

当CP属性依赖于.property('columns.@each.isLoaded')时:

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

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

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

  - 不能依赖@each.owner.@each.name 。

 test('computed property depend on @each.height', function (assert) {
var Size = Ember.Object.extend({height: 0, width: 0});
var rectangle = Ember.Object.extend({
totalArea: Ember.computed('sizeArray.@each.height', function () {
return this.get('sizeArray').reduce(function (prev, cur) {
return prev + Ember.get(cur, 'height') * Ember.get(cur, 'width');
}, 0);
}),
sizeArray: [
Size.create({height: 10, width: 10}),
Size.create({height: 10, width: 10})
]
}).create();
var sizeArray = rectangle.get('sizeArray'); assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200"); sizeArray.pushObject(Size.create({height: 10, width: 10}));
assert.equal(rectangle.get('totalArea'), 300, "the total area should be 300 after added"); sizeArray.removeAt(0);
assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200 after removed"); sizeArray[0].set('height', 20);
assert.equal(rectangle.get('totalArea'), 300, "the total area should be 300 after 'height' changed"); sizeArray[0].set('width', 20);
assert.equal(rectangle.get('totalArea'), 300, "the total area should not be changed after 'width' changed"); sizeArray.clear();
assert.equal(rectangle.get('totalArea'), 0, "the total area should be 0 after reset rectangle.sizeArray");
});

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

2. Array.@each特性

当CP属性依赖于.property('columns.@each')时:

  - columns增加或删除元素。

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

 test('computed property depend on @each', function (assert) {
var Size = Ember.Object.extend({height: 0, width: 0});
var rectangle = Ember.Object.extend({
totalArea: Ember.computed('sizeArray.@each', function () {
return this.get('sizeArray').reduce(function (prev, cur) {
return prev + Ember.get(cur, 'height') * Ember.get(cur, 'width');
}, 0);
}),
sizeArray: [
Size.create({height: 10, width: 10}),
Size.create({height: 10, width: 10})
]
}).create();
var sizeArray = rectangle.get('sizeArray'); assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200"); sizeArray.pushObject(Size.create({height: 10, width: 10}));
assert.equal(rectangle.get('totalArea'), 300, "the total area should be 300 after added"); sizeArray.removeAt(0);
assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200 after removed"); sizeArray[0].set('height', 20);
assert.equal(rectangle.get('totalArea'), 200, "the total area should not be changed"); sizeArray.clear();
assert.equal(rectangle.get('totalArea'), 0, "the total area should be 0 after reset rectangle.sizeArray");
});

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

3. Array.[]特性

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

  - 与绑定.property(‘columns.@each') 行为相同。

 test('computed property depend on []', function (assert) {
var Size = Ember.Object.extend({height: 0, width: 0});
var rectangle = Ember.Object.extend({
totalArea: Ember.computed('sizeArray.[]', function () {
return this.get('sizeArray').reduce(function (prev, cur) {
return prev + Ember.get(cur, 'height') * Ember.get(cur, 'width');
}, 0);
}),
sizeArray: [
Size.create({height: 10, width: 10}),
Size.create({height: 10, width: 10})
]
}).create();
var sizeArray = rectangle.get('sizeArray'); assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200"); sizeArray.pushObject(Size.create({height: 10, width: 10}));
assert.equal(rectangle.get('totalArea'), 300, "the total area should be 300 after added"); sizeArray.removeAt(0);
assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200 after removed"); sizeArray[0].set('height', 20);
assert.equal(rectangle.get('totalArea'), 200, "the total area should not be changed"); sizeArray.clear();
assert.equal(rectangle.get('totalArea'), 0, "the total area should be 0 after reset rectangle.sizeArray");
});

[参考代码](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.[] & Array.@each

Array.@each返回的是一个特殊类型,这个类型便于实现绑定。

   '@each': computed(function() {
if (!this.__each) {
// ES6TODO: GRRRRR
var EachProxy = requireModule('ember-runtime/system/each_proxy')['EachProxy']; this.__each = new EachProxy(this);
} return this.__each;
})

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

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

   '[]': computed({
get(key) {
return this;
},
set(key, value) {
this.replace(0, get(this, 'length'), value);
return this;
}
}),

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

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

 var EachProxy = EmberObject.extend({

   init(content) {
this._super(...arguments);
this._content = content;
content.addArrayObserver(this); // in case someone is already observing some keys make sure they are
// added
forEach(watchedEvents(this), function(eventName) {
this.didAddListener(eventName);
}, this);
}, ...

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

四、相关引用

[Ember-@each](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)

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

  1. mvc数组绑定-jquery ajax

    var list=[];//数组 list[0]=1001; list[1]=1002; list[1]=1003; var json_data = { selected: list}; $.ajax ...

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

    新建类,int[]数组模型绑定 using System; using System.Collections.Generic; using System.Linq; using System.Web; ...

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

    <html> <body> <input type="button" name="input[]" value="按钮1 ...

  4. 使用C# .NET 将结构数组绑定到 Windows 窗体的方法

      本任务的内容 概要 要求 设计结构 向数组添加结构实例 将结构成员绑定到窗体控件 提供浏览数组的方式 分步示例 参考 概要 本文介绍如何向 Windows 窗体绑定结构数组. 该示例由一个 Win ...

  5. Spring MVC数组绑定

    需求:商品批量删除,用户在页面选择多个商品,批量删除. 关键:将页面选择(多选)的商品id,传到controller方法的形参,方法形参使用数组接收页面请求的多个商品id // 批量删除 商品信息 @ ...

  6. Ng的数组绑定

    tip:数据的定义需要在对应ts中进行,调用在html中 定义数组: ts中 public arr =["111","222","333"] ...

  7. Repeater绑定数组并显示其值

    web开发中,尤其是对于数据展示,不得不说Repeater是一个万能的控件,而且使用也很方便. 在ASP.NET中将数组绑定到Repeater中请问如何在Repeater前台页面中显示该数组的值? s ...

  8. ASP.NET MVC数组模型绑定

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

  9. MVC数组模型绑定

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

随机推荐

  1. Linux 安装node.js ---- 源码编译的方式

    一 : 普通用户: 安装前准备环境: 1.检查Linux 版本 命令: cat /etc/redhat-release 2.检查 gcc.gcc-c++ 是否安装过 命令: rpm -q gcc rp ...

  2. Hibernate QBC运算符

    HQL运算符 QBC运算符 含义 = Restrictions.eq() 等于equal <> Restrictions.ne() 不等于not   equal > Restrict ...

  3. myql数据库在cmd下,中文乱码的问题原因

    使用navicat把数据导入数据库,这些数据都是中文,导入成功,显式也正常,但是在mysql cmd下都是乱码.检查了我的mysql配置,字符编码都是utf8,包括navicat连接时候也设置过是ut ...

  4. Cordova系列(一)

    1.安装 这里推荐用npm安装cordova,至于npm的安装,网上有很多的.打开命令行,输入 npm install -g cordova 这里就安装了好了最新版的cordova,虽然绝大多数会成功 ...

  5. 公网,专用,共享独立IP介绍

    什么是IP地址? IP地址也被称为InternetProtocol地址,IP地址被用于在互联网上确定不同的计算机和设备.你可以认为它像一个邮寄信封一个家庭返回地址,只在数字和时间使用.IP地址被用于在 ...

  6. Windows系统下部署安装一个/多个Tomcat8

    首先从http://tomcat.apache.org/上下载Tomcat8.0压缩版的,解压到指定路径后即可.  第一:在Windows系统中安装部署单个Tomcat         对于这种情况, ...

  7. 百度地图定位经纬度返回4.9E-324有关问题

    1.查看你的应用是否有权限查看你的地理位置信息,有可能是你没有加上权限,或者当你第一次打开app时询问你是否给予软件权限查看你的地理位置信息,你选择了否,所以经纬度就一直返回4.9E-324 2.查看 ...

  8. RBAC用户权限管理数据库设计

    RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联.简单地说,一个用户拥有若干角色,每一个角色拥有若干权限.这样,就构造成“用户-角色- ...

  9. DUT Star Round2

    A.Zeratu的军训游戏 Problems: 开灯问题,问无数次操作之后第n盏灯的状态 Analysis: cj:平方数有奇数个约数 Tags: Implementation B.Zeratud的完 ...

  10. STM32 使用DMA+DAC+TIMER 输出正弦波

    之前已经简单论述过,根据我个人菜鸟的了解与认识,对之前的知识进行整理回顾: DMA:我的理解就是一个通道,或者是一座桥梁.在静态内存到静态内存,或者外设到静态内存间的一个通讯的通道.建立这个通道的好处 ...