码迷,mamicode.com
首页 > Web开发 > 详细

Emberjs之ComputedProperty

时间:2015-06-04 00:53:14      阅读:302      评论:0      收藏:0      [点我收藏+]

标签:

Computed Property (简称CP)

1. What is CP?

In a nutshell, computed properties let you declare functions as properties. You create one by defining a computed property as a function, which Ember will automatically call when you ask for the property. You can then use it the same way you would any normal, static property.

App.Person = Ember.Object.extend({
  // these will be supplied by `create`
  firstName: null,
  lastName: null,

  fullName: function() {
    return this.get(‘firstName‘) + ‘ ‘ + this.get(‘lastName‘);
  }.property(‘firstName‘, ‘lastName‘)
});

var ironMan = App.Person.create({
  firstName: "Tony",
  lastName:  "Stark"
});

ironMan.get(‘fullName‘); // "Tony Stark"

这里fullName是一个CP,依赖于firstName,lastName.

另:CP也可以这样写,建议如下写法,避免“禁用”function的property扩展带来的问题。

 fullName: Ember.computed(‘firstName‘, ‘lastName‘, function() {
    return this.get(‘firstName‘) + ‘ ‘ + this.get(‘lastName‘);
  })

2. CP中名词描述定义

2.1 CP:Computed Property。

2.2 CP属性:上文例子中的‘fullName’。

2.3 CP所依赖的源属性:上文例子中的‘firstName’、‘lastName’。

2.4 CP的回调方法:上文例子中的function(){......}方法。

2.5 CP属性的Setter/Getter:

 

3. CP重要原则(特性)

3.1 只有当获取或设置CP属性时,才可能会触发CP的回调方法,也就是说CP属性是属于‘懒加载’的方式(使用时加载的行为)。

3.2 当CP属性依赖于.property(‘person{name,age}‘)时,仅当person.name或person.age发生改变时改变,其余方式均不可以更新CP属性。

3.3 当CP属性依赖于.property(‘columns.@each.isLoaded‘)时, 以下四种情况会发生变化:

  • columns里面任何一个元素的isLoaded属性发生变化时,CP属性会重新计算(当CP属性被设置或获取时)。
  • columns增加元素或者删除子元素时,CP属性会重新计算(当CP属性被设置或获取时)。
  • columns数组本身被重新赋值,会触发CP属性重新计算(当CP属性被设置或获取时)。
  • columns数组元素中其他属性更改不会触发CP属性重新计算(当CP属性被设置或获取时)。
  • Note that @each only works one level deep. You cannot use nested forms like todos.@each.owner.nameor todos.@each.owner.@each.name.

3.4 当CP属性依赖于.property(‘columns.@each‘)时,其行为会发生如下变化:

  • 当columns增加或删除元素时,CP属性会重新计算(当CP属性被设置或获取时)。
  • 当columns自身被替换或重新赋值时,CP属性会重新计算(当CP属性被设置或获取时)。

3.5 当CP属性依赖于.property(‘columns.[]‘)时,其行为会发生如下变化:

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

3.6 当通过set方法设置CP属性时,然后调用get方法获取CP属性,则不调用CP回调,set时值被缓存。

3.7 对象在继承CP属性时,保持CP属性的独立性、互不干扰,并且重写后会改写CP依赖关系,变更依赖源。

3.8 当存在两个互相依赖的CP属性时,仅仅发生三次属性变更。

3.9 不要将CP的依赖属性附着在另一个CP属性上。

3.10 当CP属性依赖于对象列表时,例如.property(‘a.b.c.d‘)上时,节点上任意对象发生变化时,均会重新计算属性(当调用CP属性时)。

4. CP宏定义

总原则:CP依赖的属性发生改变后,当调用CP值时,会触发宏的重新计算(触发回调function)。

 

Ember.computed.empty: empty(属性名)返回bool

Ember.computed.not: not(属性名)返回bool

Ember.computed.alias:alias(属性名),双向绑定, alias不要依赖于一个CP.

Ember.computed.defaultTo: 如果CP属性为null,则读取依赖属性值一次

Ember.computed.match(属性名, 匹配字符串)

Ember.computed.gt(属性名,数字)大于返回bool

Ember.computed.gte(属性名,数字)大于或等于bool

Ember.computed.and(属性名,属性名) 并集

Ember.computed.or(属性名, 属性名) 交集

Ember.computed.collect( 数组 ) 匹配所有项,没有相则为null

Ember.computed.oneWay(属性名) 单方向从到PC属性,并且仅执行一次。

Ember.computed.readOnly(属性名) CP属性不允许被设置,但CP所依赖的源属性更新CP值。

 更多宏定义请参考这里:http://emberjs.com/api/classes/Ember.computed.html#method_alias

5. CP使用场景

5.1 在我们使用的对象上,希望使用一个属性值监听一个或多个属性的变更,或者CP属性强依赖于某些属性,而且还能缓存CP属性值,减少性能损耗。(CP特性请参考3.1)

5.2 CP可以依赖在一个对象的多个属性上, 特别是绑定在集合元素上甚至监听集合元素内部某一属性,但层次有限制。例如.property(‘person{name,age}‘)或.property(‘pencilBox.[]‘, penBox.@each.color‘, penBox.@each)。(CP特性请参考3.2、3.3、3.4)

5.3 Ember.computed.alias作用于两个强关联对象的双向绑定,并提供缓存机制。

5.4 通过CP来组合属性,CP属性回调中不能有边界效应等循环、异步方法。

6. 示例代码

技术分享
var computedCount = 0;
module(‘CP test‘, {
  beforeEach: function () {
    computedCount = 0;
  },

  afterEach: function () {
  }
});

test(‘Should change dependency property value when CP property changed and binding style is Ember.computed.alias‘, function(assert){
  var Person = Ember.Object.extend({
    name: ‘Alex Matchneer‘,
    nomen: Ember.computed.alias(‘name‘)
  });

  var alex = Person.create();
  alex.get(‘nomen‘); // ‘Alex Matchneer‘
  alex.get(‘name‘);  // ‘Alex Matchneer‘

  alex.set(‘nomen‘, ‘@machty‘);
  assert.ok(alex.get(‘name‘), ‘@machty‘);
});

test(‘Should change CP property when dependency property changed changed and binding style is Ember.computed.alias‘, function(assert){
  var Person = Ember.Object.extend({
    name: ‘Alex Matchneer‘,
    nomen: Ember.computed.alias(‘name‘)
  });

  var alex = Person.create();
  alex.get(‘nomen‘); // ‘Alex Matchneer‘
  alex.get(‘name‘);  // ‘Alex Matchneer‘

  alex.set(‘name‘, ‘@machty‘);
  assert.ok(alex.get(‘nomen‘), ‘@machty‘);
});

test(‘Should compute fullName when dependency property of columns inner item changed and binding style is columns.@each.isDone‘, function(assert) {
  var Person = Ember.Object.extend({
    columns: [
      Ember.Object.create({ isDone: true, isLoading: false }),
      Ember.Object.create({ isDone: false, isLoading: false  }),
      Ember.Object.create({ isDone: true , isLoading: false })
    ],
    fullName: Ember.computed(function(key, value) {
      computedCount++;
      return this.columns.length;
    }).property(‘columns.@each.isDone‘)
  });

  var client = Person.create();
  client.get(‘fullName‘);
  assert.ok(computedCount === 1);

  client.get(‘columns‘).objectAt(1).set(‘isLoading‘, true);
  client.get(‘fullName‘);
  assert.ok(computedCount === 1);


  client.get(‘columns‘).objectAt(1).set(‘isDone‘, true);
  client.get(‘fullName‘);
  assert.ok(computedCount === 2);

  client.get(‘columns‘).removeAt(1);
  client.get(‘fullName‘);
  assert.ok(computedCount === 3);

  client.get(‘columns‘).addObject(Ember.Object.create({ isDone: true, isLoading: false }));
  client.get(‘fullName‘);
  assert.ok(computedCount === 4);

  client.set(‘columns‘,[]);
  client.get(‘fullName‘);
  assert.ok(computedCount === 5);
});

test(‘Should compute fullName when dependency property of columns inner item changed and binding style is columns.@each‘, function(assert) {
  var Person = Ember.Object.extend({
    columns: [
      Ember.Object.create({ isDone: true, isLoading: false }),
      Ember.Object.create({ isDone: false, isLoading: false  }),
      Ember.Object.create({ isDone: true , isLoading: false })
    ],
    fullName: Ember.computed(function(key, value) {
      computedCount++;
      return this.columns.length;
    }).property(‘columns.@each‘)
  });

  var client = Person.create();
  client.get(‘fullName‘);
  console.log( "After get fullName, computedCount should be equal "+ computedCount);

  client.get(‘columns‘).objectAt(1).set(‘isLoading‘, true);
  client.get(‘fullName‘);
  console.log("After set isLoading to true, computedCount should be equal "+ computedCount);


  client.get(‘columns‘).objectAt(1).set(‘isDone‘, true);
  client.get(‘fullName‘);
  console.log( "After set isDone to true, computedCount should be equal "+ computedCount);

  client.get(‘columns‘).removeAt(1);
  client.get(‘fullName‘);
  console.log( "After remove column item, computedCount should be equal "+ computedCount);

  client.get(‘columns‘).addObject(Ember.Object.create({ isDone: true, isLoading: false }));
  client.get(‘fullName‘);
  console.log( "After add new object, computedCount should be equal "+ computedCount);

  client.set(‘columns‘,[]);
  client.get(‘fullName‘);
  console.log( "After replace itself, computedCount should be equal "+ computedCount);

  assert.ok(true);
});

test(‘Should compute fullName when dependency property of columns inner item changed and binding style is columns.[]‘, function(assert) {
  var Person = Ember.Object.extend({
    columns: [
      Ember.Object.create({ isDone: true, isLoading: false }),
      Ember.Object.create({ isDone: false, isLoading: false  }),
      Ember.Object.create({ isDone: true , isLoading: false })
    ],
    fullName: Ember.computed(function(key, value) {
      computedCount++;
      return this.columns.length;
    }).property(‘columns.[]‘)
  });

  var client = Person.create();
  client.get(‘fullName‘);
  console.log( "After get fullName, computedCount should be equal "+ computedCount);

  client.get(‘columns‘).objectAt(1).set(‘isLoading‘, true);
  client.get(‘fullName‘);
  console.log("After set isLoading to true, computedCount should be equal "+ computedCount);


  client.get(‘columns‘).objectAt(1).set(‘isDone‘, true);
  client.get(‘fullName‘);
  console.log( "After set isDone to true, computedCount should be equal "+ computedCount);

  client.get(‘columns‘).removeAt(1);
  client.get(‘fullName‘);
  console.log( "After remove column item, computedCount should be equal "+ computedCount);

  client.get(‘columns‘).addObject(Ember.Object.create({ isDone: true, isLoading: false }));
  client.get(‘fullName‘);
  console.log( "After add new object, computedCount should be equal "+ computedCount);

  client.set(‘columns‘,[]);
  client.get(‘fullName‘);
  console.log( "After replace itself, computedCount should be equal "+ computedCount);

  assert.ok(true);
});
View Code

 

Emberjs之ComputedProperty

标签:

原文地址:http://www.cnblogs.com/cuiyansong/p/4550150.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!