Ember 計(jì)算屬性

2018-12-11 14:49 更新

簡(jiǎn)單的計(jì)算屬性

簡(jiǎn)單地來(lái)說(shuō),計(jì)算屬性就是將函數(shù)聲明為屬性,就類(lèi)似于調(diào)用了一個(gè)函數(shù),Ember會(huì)自動(dòng)調(diào)用這個(gè)函數(shù)。計(jì)算屬性最大的特點(diǎn)就是能自動(dòng)檢測(cè)變化,及時(shí)更新數(shù)據(jù)。

Person = Ember.Object.extend({
    firstName: null,
    lastName: null,

    
    //  fullName 就是一個(gè)計(jì)算屬性
    fullName: Ember.computed('firstName', 'lastName', function() {
        return this.get('firstName') + ", " + this.get('lastName');
    })
});


//  實(shí)例化同時(shí)傳入?yún)?shù)
var piter = Person.create({
    firstName: 'chen',
    lastName: 'ubuntuvim'
});
console.log(piter.get('fullName'));  // output >>   chen, ubuntuvim

計(jì)算屬性其實(shí)就是一個(gè)函數(shù),如果你接觸過(guò)就jQuery、Extjs相信你會(huì)非常熟悉,在這兩個(gè)框架中函數(shù)就是這么定義的。只不過(guò)在Ember中,把這種函數(shù)當(dāng)做屬性來(lái)處理,并且可以通過(guò)get獲取函數(shù)的返回值。

計(jì)算屬性鏈

Ember程序中,計(jì)算屬性還能調(diào)用另外一個(gè)計(jì)算屬性,形成計(jì)算屬性鏈,也可以用于擴(kuò)展某個(gè)方法。在上一實(shí)例的基礎(chǔ)上增加一個(gè)description()方法。

Person = Ember.Object.extend({
    firstName: null,
    lastName: null,
    age: null,
    county: null,

    
    //  fullName 就是一個(gè)計(jì)算屬性
    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');
    })
});


//  實(shí)例化同時(shí)傳入?yún)?shù)
var piter = Person.create({
    firstName: 'chen',
    lastName: 'ubuntuvim',
    age: 25,
    county: 'china'
});
console.log(piter.get('description'));  // output >>   chen, ubuntuvim

當(dāng)用戶(hù)使用set方法改變firstName的值,然后再調(diào)用get('description')得到的值也是更新后的值。

重寫(xiě)計(jì)算屬性的get、set方法

注意要把重寫(xiě)的屬性作為參數(shù)傳入computed方法,要區(qū)別計(jì)算屬性的定義方法,定義的時(shí)候computed方法的最后一個(gè)參數(shù)是一個(gè)function,而重寫(xiě)的時(shí)候最后一個(gè)參數(shù)是一個(gè)hash。

//    重寫(xiě)計(jì)算屬性的get、set方法
Person = Ember.Object.extend({
    firstName: null,
    lastName: null,

    
    //  重寫(xiě)計(jì)算屬性fullName的get、set方法
    fullName: Ember.computed('firstName', 'lastName', {
        get(key) {
            return this.get('firstName') + "," + this.get('lastName');
        },
        set(key, value) {
            //  這個(gè)官方文檔使用的代碼,但是我運(yùn)行的時(shí)候出現(xiàn) Uncaught SyntaxError: Unexpected token [  這個(gè)錯(cuò)誤,不知道是否是缺少某個(gè)文件,后續(xù)會(huì)補(bǔ)上;
//            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);

            
        }
    }),
//    對(duì)于普通的屬性無(wú)法重寫(xiě)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'));

運(yùn)行結(jié)果

計(jì)算屬性值的統(tǒng)計(jì)

我們經(jīng)常會(huì)遇到這種情況:某個(gè)計(jì)算屬性值是依賴(lài)某個(gè)數(shù)組或者其他對(duì)象的,比如在Embertodos這個(gè)例子中有這樣的一段代碼。

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');
  })
});

計(jì)算屬性remaining的值于依賴(lài)數(shù)組todos。在這里還有個(gè)知識(shí)點(diǎn):在上述代碼computed()方法里有一個(gè)todos.@each.isDone這樣的鍵,里面包含了一個(gè)特別的鍵@each(后面還會(huì)看到更特別的鍵[])。需要注意的是這種鍵不能嵌套并且是只能獲取一個(gè)層次的屬性。比如todos.@each.foo.name(獲取多層次屬性,這里是先得到foo再獲取name)或者todos.@each.owner.@each.name(嵌套)這兩種方式都是不允許的。

在如下4種情況Ember會(huì)自動(dòng)更新綁定的計(jì)算屬性值:<br> 1.在todos數(shù)組中任意一個(gè)對(duì)象的isDone屬性值發(fā)生變化的時(shí)候; 2.往todos數(shù)組新增元素的時(shí)候; 3.從todos數(shù)組刪除元素的時(shí)候; 4.在控制器中todos數(shù)組被改變?yōu)槠渌臄?shù)組的時(shí)候;

比如下面代碼演示的結(jié)果;

Task = Ember.Object.extend({
  isDone: false  //  默認(rèn)為false
}); 


WorkerLists = Ember.Object.extend({
  //  定義一個(gè)Task對(duì)象數(shù)組
  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');
    //  先查詢(xún)屬性isDone值為false的對(duì)象,再返回其數(shù)量
    return lists.filterBy('isDone', false).get('length');
  })
});


// 如下代碼使用到的API請(qǐng)查看: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');  //  得到對(duì)象內(nèi)的數(shù)組


// -----  演示第一種情況: 1. 在todos數(shù)組中任意一個(gè)對(duì)象的isDone屬性值發(fā)生變化的時(shí)候;
//  修改數(shù)組一個(gè)元素的isDone的 值
var item1 = lists.objectAt(3);  //  得到第4個(gè)元素 objectAt()方法是Ember為我們提供的
// console.log('item1 = ' + item1);
item1.set('isDone', false);
console.log('2,>> Not complete lenght is ' + wl.get('remaining'));  //  output 4


//  --------- 2.  往todos數(shù)組新增元素的時(shí)候;
lists.pushObject(Task.create({ isDone: false }));  //新增一個(gè)isDone為false的對(duì)象
console.log('3,>> Not complete lenght is ' + wl.get('remaining'));  //  output 5


//  --------- 3.  從todos數(shù)組刪除元素的時(shí)候;
lists.removeObject(item1);  // 刪除了一個(gè)元素
console.log('4,>> Not complete lenght is ' + wl.get('remaining'));  //  output 4


//  --------- 4.  在控制器中todos數(shù)組被改變?yōu)槠渌臄?shù)組的時(shí)候;
//  創(chuàng)建一個(gè)Controller
TodosController = Ember.Controller.extend({
  // 在控制器內(nèi)定義另外一個(gè)Task對(duì)象數(shù)組
  todosInController: [
    Task.create({ isDone: false }),
    Task.create({ isDone: true })
  ],
  //  使用鍵”@each.isDone“遍歷得到的filterBy()方法過(guò)濾后的對(duì)象的isDone屬性
  remaining: function() {
    //  remaining()方法返回的是控制器內(nèi)的數(shù)組
    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

代碼演示的結(jié)果

上述的情況中,我們對(duì)數(shù)組對(duì)象的是關(guān)注點(diǎn)是在對(duì)象的屬性上,但是實(shí)際中往往很多情況我們并不關(guān)系對(duì)象內(nèi)的屬性是否變化了,而是把數(shù)組元素作為一個(gè)整體對(duì)象處理(比如數(shù)組元素個(gè)數(shù)的變化)。相比上述的代碼下面的代碼檢測(cè)的是數(shù)組對(duì)象元素的變化,而不是對(duì)象的isDone屬性的變化。在這種情況你可以看看下面例子,在例子中使用鍵[]代替鍵@each。從鍵的變化也可以看出他們的不同之處。

Task = Ember.Object.extend({
  isDone: false,  //  默認(rèn)為false
  name: 'taskName',
  //  為了顯示結(jié)果方便,重寫(xiě)toString()方法
  toString: function() {
    return '[name = '+this.get('name')+', isDone = '+this.get('isDone')+']';
  }
}); 


WorkerLists = Ember.Object.extend({
  //  定義一個(gè)Task對(duì)象數(shù)組
  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);

代碼演示的結(jié)果

Ember.computed這個(gè)組件中有很多使用鍵[]實(shí)現(xiàn)的方法。當(dāng)你想創(chuàng)建一個(gè)計(jì)算屬性是數(shù)組的時(shí)候特別適用。你可以使用Ember.computed.map來(lái)構(gòu)建你的計(jì)算屬性。

const Hamster = Ember.Object.extend({
  chores: null,
  excitingChores: Ember.computed('chores.[]', function() { //告訴Ember chores是一個(gè)數(shù)組
    return this.get('chores').map(function(chore, index) {
      //return `${index} --> ${chore.toUpperCase()}`;  //  可以使用${}表達(dá)式,并且在表達(dá)式內(nèi)可以直接調(diào)用js方法
      return `${chore}`;  //返回元素值
    });
  })
});


//  為數(shù)組賦值
const hamster = Hamster.create({
  //  名字chores要與類(lèi)Hamster定義指定數(shù)組的名字一致
  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還提供了另外一種方式去定義數(shù)組類(lèi)型的計(jì)算屬性。

const Hamster = Ember.Object.extend({
  chores: null,
  excitingChores: Ember.computed('chores.[]', function() {
    return this.get('chores').map(function(chore, index) {
      //return `${index} --> ${chore.toUpperCase()}`;  //  可以使用${}表達(dá)式,并且在表達(dá)式內(nèi)可以直接調(diào)用js方法
      return `${chore}`;  //返回元素值
    });
  })
});


//  為數(shù)組賦值
const hamster = Hamster.create({
  //  名字chores要與類(lèi)Hamster定義指定數(shù)組的名字一致
  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'));

<br> 博文完整代碼放在Github(博文經(jīng)過(guò)多次修改,博文上的代碼與github代碼可能又出入,不過(guò)影響不大?。?,如果你覺(jué)得博文對(duì)你有點(diǎn)用在github項(xiàng)目上給我個(gè)star吧。您的肯定對(duì)我來(lái)說(shuō)是最大的動(dòng)力??!

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)