2014-03-04 135 views
0

我無法使用Ember數據,因爲我們的數據模型有點複雜(例如,並非所有東西都有一個ID),我不喜歡緩存和其他一些關於它的事情;然而,我確實喜歡它的模型有狀態標誌(isNew,isDeleting),所以我試圖實現一些功能。如何在動態子屬性更改時更新Ember計算屬性

基本要點是,無論何時從服務器檢索模型,該數據都存儲在_data對象中。每當用戶更改某個值時,它就存儲在_attributes對象中。通過比較兩者,我可以看到模型是否髒。屬性使用app.attr方法定義。

app.Person = app.Model.extend({ 
    "id": app.attr("number"), 
    "name": app.attr("string"), 
    "age": app.attr("number") 
}); 

attr函數與ember數據非常相似。

function getValue(record, key, options) { 
    var attrs = get(record, "_attributes"), 
     data = get(record, "_data"); 
    if (attrs.hasOwnProperty(key)) { 
     return attrs[key]; 
    } else if (data.hasOwnProperty(key)) { 
     return data[key]; 
    } else if (typeof options.defaultValue === "function") { 
     return options.defaultValue(); 
    } 
    return options.defaultValue; // may be undefined 
} 

app.attr = function(type, options) { 
    options = options || {}; 
    var meta = { 
     "type": type, 
     "isAttribute": true, 
     "options": options 
    }; 
    return function(key, value) { 
     if (arguments.length > 1) { 
      if (key === "id") { 
       console.error("id is readonly on "+this.constructor.toString()); 
      } else { 
       if (get(this, "_data."+key) === value) { 
        delete get(this, "_attributes")[key]; // not sure if this really works? seems to from my testing. set(this, "_attributes."+key, undefined) literally set undefined, which did not work 
       } else { 
        set(this, "_attributes."+key, value); 
       } 
      } 
     } 
     return getValue(this, key, options); 
    }.property("_data").meta(meta); 
}; 

然後在我的模型上,我有一些像isNew和isDirty的標誌。 isDirty作爲一個計算屬性是有意義的,但只要有任何屬性發生變化就需要更新。我想也許.property(「_ attributes。*」)會這樣做,但它不。我很難過。我試圖讓它揮發..這確保我得到正確的值時明確要求它,但模板不會更新時,底層屬性更改。

對於我上面的模型,可以使用.property(「id」,「name」,「age」)但適合模型的模型。我能夠在模型的init中獲得當前模型的屬性,但是我找不到動態改變它們的方法。

這裏是我的模型:

app.Model = Ember.Object.extend({ 
    "_attributes": null, 
    "_data": null, 

    "isDeleted": false, 
    "isNew": false, 
    "isSaving": false, 

    "isDirty": function() { 
     var attrs = get(this, "_attributes"); 
     for (k in attrs) { 
      if (attrs.hasOwnProperty(k)) { 
       return true; 
      } 
     } 
     return false; 
    }.property("_attributes").readOnly(), // NOTE: not quite right 

    "init": function() { 
     this._super(); 
     set(this, "_data", {}); 
     set(this, "_attributes", {}); 

     var attrs = []; 
     this.constructor.eachAttribute(function(name, meta) { 
      attrs.push(name); 
     }); 
     // NOTE: Can I do anything to attach all the attrs to isDirty here? 
    } 
}); 

app.Model.reopenClass({ 
    "attributes": Ember.computed(function() { 
     var map = Ember.Map.create(); 
     this.eachComputedProperty(function(name, meta) { 
      if (meta.isAttribute) { 
       meta.name = name; 
       map.set(name, meta); 
      } 
     }); 
     return map; 
    }), 
    "eachAttribute": function(callback, binding) { 
     get(this, "attributes").forEach(function(name, meta) { 
      callback.apply(binding, arguments); 
     }, binding); 
    } 
}); 

我創建了一個用的jsfiddle的代碼和我的測試:http://jsfiddle.net/2uCn3/3/

所以任何想法我如何能得到isDirty更新每當一個屬性的變化? 相似(在jsFiddle中可以看到),如果_data.someProperty被直接更改,attr仍然會返回舊的默認值,所以我也做了一個測試..但它本質上是同一個問題。

我認爲有一個計數器像_attributesChanged和使用incrementProperty但它似乎不可靠,看起來殘酷。

我還考慮將_attributes和_data轉換爲數組,然後可能@each會有一些用處。

希望有一個更清潔的方法。我仍然試圖弄清楚如何使用ember數據。

更新:如果您直接操作_data.property,我發現Ember Data也存在錯誤,我想出了一種解決方法,但GJK的解決方案無限更好。

回答

3

我爲Ember編寫了自己的持久層,我解決它的方法是每次更改_attributes對象時都調用this.notifyPropertyChange('_attributes')。我看不到你的所有代碼,但是如果你限制編輯_attributes只是一些增變器方法,那麼你只需要在3或4個地方調用它。那麼對於你的計算屬性,你可以只取決於_attributes屬性。

在你的代碼中,只需在attr函數中調用它就足夠了。 (假設這是修改_attributes內容的唯一方法。)

if (get(this, "_data."+key) === value) { 
    delete get(this, "_attributes")[key]; 
    this.notifyPropertyChange('_attributes'); 
} else { 
    set(this, "_attributes."+key, value); 
    this.notifyPropertyChange('_attributes'); 
}