简单地来讲,计算属性就是将函数声明为属性。其实就是就是在类扩展的时候为类定义的属性。html
Person = Ember.Object.extend({ firstName: null, lastName: null, // fullName 就是一个计算属性 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')); // output >> chen, ubuntuvim
计算属性其实就是一个函数,若是你接触过就jQuery、Extjs相信你回很是熟悉,在这两个框架中函数就是这么定义的。只不过在Ember中,把这种函数当作属性来处理,而且能够经过get获取函数的返回值。ubuntu
在Ember程序中,计算属性还能调用另一个计算属性,造成计算属性链,也能够用于扩展某个方法。在上一实例的基础上增长一个description()方法。vim
Person = Ember.Object.extend({ firstName: null, lastName: null, age: null, county: null, // fullName 就是一个计算属性 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')); // output >> chen, ubuntuvim
当用户使用set方法改变firstName的值,而后再调用get('description')获得的值也是更新后的值。api
注意要把从新的属性做为参数传入computed方法,要区别计算属性的定义方法,定义的时候computed方法的最后一个参数是一个function,而重写的时候最后一个参数是一个hash。数组
// 重写计算属性的get、set方法 Person = Ember.Object.extend({ firstName: null, lastName: null, // 重写计算属性fullName的get、set方法 fullName: Ember.computed('firstName', 'lastName', { get(key) { return this.get('firstName') + "," + this.get('lastName'); }, set(key, value) { // 这个官方文档使用的代码,可是我运行的时候出现 Uncaught SyntaxError: Unexpected token [ 这个错误,不知道是不是缺乏某个文件,后续会补上; // console.log("value = " + value); // var [ firstName, lastName ] = value.split(/\s+/); var firstName = value.split(/\s+/)[0]; var lastName = value.split(/\s+/)[1]; this.set('firstName', firstName); this.set('lastName', lastName); } }), // 对于普通的属性没法重写get、set方法 // firstName: Ember.computed('firstName', { // get(key) { // return this.get('firstName') + "@@"; // }, // set(key, value) { // this.set('firstName', value); // } // }) }); var jack = Person.create(); jack.set('fullName', "james kobe"); console.log(jack.get('firstName')); console.log(jack.get('lastName'));
咱们常常会遇到这种状况:某个计算属性值是依赖某个数组或者其余对象的,好比在Ember JS 的todos这个例子中有这样的一段代码。app
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(嵌套)这两种方式都是不容许的。“todos.@each.foo.name”。框架
在以下4种状况Ember会自动更新绑定的计算属性值:函数
1. 在todos数组中任意一个对象的isDone属性值发生变化的时候;网站
2. 往todos数组新增元素的时候;this
3. 从todos数组删除元素的时候;
4. 在控制器中todos数组被改变为其余的数组的时候;
好比下面代码演示的结果;
Task = Ember.Object.extend({ isDone: false // 默认为false }); WorkerLists = Ember.Object.extend({ // 定义一个Task对象数组 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'); // 查询属性isDone值为false的对象,并返回其数量 return lists.filterBy('isDone', false).get('length'); }) }); // 以下代码使用到的API请查看:http://emberjs.com/api/classes/Ember.MutableArray.html var wl = WorkerLists.create(); // 全部isDone属性值未作任何修改 console.log('1,>> Not complete lenght is ' + wl.get('remaining')); // output 3 var lists = wl.get('lists'); // 获得对象内的数组 // ----- 演示第一种状况: 1. 在todos数组中任意一个对象的isDone属性值发生变化的时候; // 修改数组一个元素的isDone的 值 var item1 = lists.objectAt(3); // 获得第4个元素 objectAt()方法是Ember为咱们提供的 // console.log('item1 = ' + item1); item1.set('isDone', false); console.log('2,>> Not complete lenght is ' + wl.get('remaining')); // output 4 // --------- 2. 往todos数组新增元素的时候; lists.pushObject(Task.create({ isDone: false })); //新增一个isDone为false的对象 console.log('3,>> Not complete lenght is ' + wl.get('remaining')); // output 5 // --------- 3. 从todos数组删除元素的时候; lists.removeObject(item1); // 删除了一个元素 console.log('4,>> Not complete lenght is ' + wl.get('remaining')); // output 4 // --------- 4. 在控制器中todos数组被改变为其余的数组的时候; // 建立一个Controller TodosController = Ember.Controller.extend({ // 在控制器内定义另一个Task对象数组 todosInController: [ Task.create({ isDone: false }), Task.create({ isDone: true }) ], // 使用键”@each.isDone“遍历获得的filterBy()方法过滤后的对象的isDone属性 remaining: function() { // remaining()方法返回的是控制器内的数组 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); // output 1
上述的状况中,咱们对数组对象的是关注点是在对象的属性上,可是实际中每每不少状况咱们并不关系对象内的属性是否变化了,而是把数组元素做为一个总体对象处理,相比上述的代码咱们只关心数组对象元素的变化,而不是对象的isDone属性的变化。在这种状况你能够看看下面例子,在例子中使用键“[]”代替键“@each.foo”。从键的变化也能够看出他们的不一样之处。
Task = Ember.Object.extend({ isDone: false, // 默认为false name: 'taskName', // 为了显示结果方便,重写toString()方法 toString: function() { return '[name = '+this.get('name')+', isDone = '+this.get('isDone')+']'; } }); WorkerLists = Ember.Object.extend({ // 定义一个Task对象数组 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(); // 全部isDone属性值未作任何修改 var index = 1; wl.set('index', index); console.log('Get '+wl.get('indexOfSelectedTodo').toString()+' by index ' + index);
Ember.computed这个组件中有不少使用键“[]”实现的方法。当你想建立一个计算属性是数组的时候特别适用。你可使用Ember.computed.map来构建你的计算属性。
const Hamster = Ember.Object.extend({ chores: null, excitingChores: Ember.computed('chores.[]', function() { //告诉Ember chores是一个数组 return this.get('chores').map(function(chore, index) { //return `${index} --> ${chore.toUpperCase()}`; // 可使用${}表达式,而且在表达式内能够直接调用js方法 return `${chore}`; //返回元素值 }); }) }); // 为数组赋值 const hamster = Hamster.create({ // 名字chores要与类Hamster定义指定数组的名字一致 chores: ['First Value', 'write more unit tests'] }); console.log(hamster.get('excitingChores')); hamster.get('chores').pushObject("Add item test"); //add an item to chores array console.log(hamster.get('excitingChores'));
Ember还提供了另一种方式去定义数组类型的计算属性。
const Hamster = Ember.Object.extend({ chores: null, excitingChores: Ember.computed('chores.[]', function() { return this.get('chores').map(function(chore, index) { //return `${index} --> ${chore.toUpperCase()}`; // 可使用${}表达式,而且在表达式内能够直接调用js方法 return `${chore}`; //返回元素值 }); }) }); // 为数组赋值 const hamster = Hamster.create({ // 名字chores要与类Hamster定义指定数组的名字一致 chores: ['First Value', 'write more unit tests'] }); console.log(hamster.get('excitingChores')); hamster.get('chores').pushObject("Add item test"); //add an item to chores array console.log(hamster.get('excitingChores'));