简单的计算属性 简单地来说,计算属性就是将函数声明为属性,就类似于调用了一个函数,Ember
会自动调用这个函数。计算属性最大的特点就是能自动检测变化,及时更新数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Person = Ember.Object.extend({ firstName: null , lastName: null , fullName: Ember.computed('firstName' , 'lastName' , function ( ) { return this .get('firstName' ) + ", " + this .get('lastName' ); }) }); var piter = Person.create({ firstName: 'chen' , lastName: 'ubuntuvim' }); console .log(piter.get('fullName' ));
计算属性其实就是一个函数,如果你接触过就jQuery、Extjs
相信你回非常熟悉,在这两个框架中函数就是这么定义的。只不过在Ember
中,把这种函数当做属性来处理,并且可以通过get获取函数的返回值。
计算属性链 在Ember
程序中,计算属性还能调用另外一个计算属性,形成计算属性链,也可以用于扩展某个方法。在上一实例的基础上增加一个description()
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Person = Ember.Object.extend({ firstName: null , lastName: null , age: null , county: null , fullName: Ember.computed('firstName' , 'lastName' , function ( ) { return this .get('firstName' ) + ", " + this .get('lastName' ); }), description: Ember.computed('fullName' , 'age' , 'county' , function ( ) { return this .get('fullName' ) + " age " + this .get('age' ) + " county " + this .get('county' ); }) }); var piter = Person.create({ firstName: 'chen' , lastName: 'ubuntuvim' , age: 25 , county: 'china' }); console .log(piter.get('description' ));
当用户使用set
方法改变firstName
的值,然后再调用get('description')
得到的值也是更新后的值。
重写计算属性的get、set方法 注意要把重写的属性作为参数传入computed
方法,要区别计算属性的定义方法,定义的时候computed
方法的最后一个参数是一个function
,而重写的时候最后一个参数是一个hash
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 Person = Ember.Object.extend({ firstName: null , lastName: null , fullName: Ember.computed('firstName' , 'lastName' , { get(key) { return this .get('firstName' ) + "," + this .get('lastName' ); }, set(key, value) { var firstName = value.split(/\s+/ )[0 ]; var lastName = value.split(/\s+/ )[1 ]; this .set('firstName' , firstName); this .set('lastName' , lastName); } }), }); var jack = Person.create(); jack.set('fullName' , "james kobe" ); console .log(jack.get('firstName' ));console .log(jack.get('lastName' ));
计算属性值的统计 我们经常会遇到这种情况:某个计算属性值是依赖某个数组或者其他对象的,比如在Ember
的todos
这个例子中有这样的一段代码。
1 2 3 4 5 6 7 8 9 10 11 export default Ember.Controller.extend({ todos: [ Ember.Object.create({ isDone : true }), Ember.Object.create({ isDone : false }), Ember.Object.create({ isDone : true }) ], remaining: Ember.computed('todos.@each.isDone' , function ( ) { var todos = this .get('todos' ); return todos.filterBy('isDone' , false ).get('length' ); }) });
计算属性remaining
的值于依赖数组todos
。在这里还有个知识点:在上述代码computed()
方法里有一个todos.@each.isDone
这样的键,里面包含了一个特别的键@each
(后面还会看到更特别的键[]
)。需要注意的是这种键不能嵌套并且是只能获取一个层次的属性。比如todos.@each.foo.name
(获取多层次属性,这里是先得到foo再获取name
)或者todos.@each.owner.@each.name
(嵌套)这两种方式都是不允许的。
在如下4种情况Ember
会自动更新绑定的计算属性值: 1.在todos
数组中任意一个对象的isDone
属性值发生变化的时候; 2.往todos
数组新增元素的时候; 3.从todos
数组删除元素的时候; 4.在控制器中todos
数组被改变为其他的数组的时候;
比如下面代码演示的结果;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 Task = Ember.Object.extend({ isDone: false }); WorkerLists = Ember.Object.extend({ lists: [ Task.create({ isDone : false }), Task.create({ isDone : true }), Task.create(), Task.create({ isDone : true }), Task.create({ isDone : true }), Task.create({ isDone : true }), Task.create({ isDone : false }), Task.create({ isDone : true }) ], remaining: Ember.computed('lists.@each.isDone' , function ( ) { var lists = this .get('lists' ); return lists.filterBy('isDone' , false ).get('length' ); }) }); var wl = WorkerLists.create();console .log('1,>> Not complete lenght is ' + wl.get('remaining' )); var lists = wl.get('lists' ); var item1 = lists.objectAt(3 ); item1.set('isDone' , false ); console .log('2,>> Not complete lenght is ' + wl.get('remaining' )); lists.pushObject(Task.create({ isDone : false })); console .log('3,>> Not complete lenght is ' + wl.get('remaining' )); lists.removeObject(item1); console .log('4,>> Not complete lenght is ' + wl.get('remaining' )); TodosController = Ember.Controller.extend({ todosInController: [ Task.create({ isDone : false }), Task.create({ isDone : true }) ], remaining: function ( ) { return this .get('todosInController' ).filterBy('isDone' , false ).get('length' ); }.property('@each.isDone' ) }); todosController = TodosController.create(); var count = todosController.get('remaining' );console .log('5,>> Not complete lenght is ' + count);
上述的情况中,我们对数组对象的是关注点是在对象的属性上,但是实际中往往很多情况我们并不关系对象内的属性是否变化了,而是把数组元素作为一个整体对象处理(比如数组元素个数的变化)。相比上述的代码下面的代码检测的是数组对象元素的变化,而不是对象的isDone
属性的变化。在这种情况你可以看看下面例子,在例子中使用键[]
代替键@each
。从键的变化也可以看出他们的不同之处。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 Task = Ember.Object.extend({ isDone: false , name: 'taskName' , toString: function ( ) { return '[name = ' +this .get('name' )+', isDone = ' +this .get('isDone' )+']' ; } }); WorkerLists = Ember.Object.extend({ lists: [ Task.create({ isDone : false , name : 'ibeginner.sinaapp.com' }), Task.create({ isDone : true , name : 'i2cao.xyz' }), Task.create(), Task.create({ isDone : true , name : 'ubuntuvim' }), Task.create({ isDone : true , name : '1527254027@qq.com' }), Task.create({ isDone : true }) ], index: null , indexOfSelectedTodo: Ember.computed('index' , 'lists.[]' , function ( ) { return this .get('lists' ).objectAt(this .get('index' )); }) }); var wl = WorkerLists.create();var index = 1 ;wl.set('index' , index); console .log('Get ' +wl.get('indexOfSelectedTodo' ).toString()+' by index ' + index);
Ember.computed
这个组件中有很多使用键[]
实现的方法。当你想创建一个计算属性是数组的时候特别适用。你可以使用Ember.computed.map
来构建你的计算属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const Hamster = Ember.Object.extend({ chores: null , excitingChores: Ember.computed('chores.[]' , function ( ) { return this .get('chores' ).map(function (chore, index ) { return `${chore} ` ; }); }) }); const hamster = Hamster.create({ chores: ['First Value' , 'write more unit tests' ] }); console .log(hamster.get('excitingChores' ));hamster.get('chores' ).pushObject("Add item test" ); console .log(hamster.get('excitingChores' ));
Ember
还提供了另外一种方式去定义数组类型的计算属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const Hamster = Ember.Object.extend({ chores: null , excitingChores: Ember.computed('chores.[]' , function ( ) { return this .get('chores' ).map(function (chore, index ) { return `${chore} ` ; }); }) }); const hamster = Hamster.create({ chores: ['First Value' , 'write more unit tests' ] }); console .log(hamster.get('excitingChores' ));hamster.get('chores' ).pushObject("Add item test" ); console .log(hamster.get('excitingChores' ));
博文完整代码放在[Github](https://github.com/ubuntuvim/my_emberjs_code)(博文经过多次修改,博文上的代码与github代码可能又出入,不过影响不大!),如果你觉得博文对你有点用在github项目上给我个`star`吧。您的肯定对我来说是最大的动力!!