2013-03-11 40 views
3

我想將呼叫保存到我的服務器,所以我目前使用Model.save()patch選項併發送changedAttributes()在Backbone.js中使用新的和未定義的值的Model.set()

我想刪除一個屬性並添加一個新屬性。 Model.set()/unset()將每次修改changedAttributes(),使得我不能將其與上述的Model.save()/patch方案一起使用。

我想我只是簡單地打電話Model.set()並傳入一個對象,其中我希望將未設定的值設置爲undefined以及我希望設置的值。

有沒有辦法,我可以unset()set()在一個去得到changedAttributes()?或者,也許可以確定組合操作集合changedAttributes()?在行動

// Currently 
var m = new Backbone.Model({ "foo": "bar" }); 
m.unset("foo"); 
console.log(m.changedAttributes()); // { "foo": undefined } 
m.set("baz", "bar"); 
console.log(m.changedAttributes()); // { "baz": "bar" } 
console.log(m.attributes); // { "baz": "bar" } 

// At this point, how do I get the combination of changed attributes? something like: { "foo": undefined, "baz": "bar" }? 
// Is that even possible? Am I doing something horribly wrong? 

//================================================================ 

// What (I think) I want is for Model.set() to remove attributes with values of undefined, so I only have to make one call and changedAttributes() will be pristine. Maybe with a option or something? 
var w = new Backbone.Model({ "foo": "bar" }); 
w.set({ "foo": undefined, "baz": "bar" }); 
console.log(w.changedAttributes()); // { "foo": undefined, "baz": "bar" } 
console.log(w.attributes); // I would like it to be { "baz": "bar" }, "foo" having been removed in the set() call. 

//================================================================ 

// I was trying to avoid processing the objects by hand. I realize that I can do something like the following. 
var h = new Backbone.Model({ "foo": "bar" }); 
var changes = { "foo": undefined, "baz": "bar" }; 
_.each(changes, function(val, key) { 
    if (_.isUndefined(val)) { 
     h.unset(key, { "silent": true }); 
    } else { 
     h.set(key, val, { "silent": true }); 
    } 
}); 
h.trigger('change'); // Trigger a change event after all the changes have been done. 
console.log(changes); // { "foo": undefined, "baz": "bar" } 
console.log(h.attributes); // { "baz": "bar" } 

小提琴的上述代碼:http://jsfiddle.net/clayzermk1/AmBfh/

似乎已經大約一年前https://github.com/documentcloud/backbone/pull/879關於這一主題的討論。看起來我想要的功能在某些時候存在。

編輯:正如@ dennis-rongo指出的那樣,我明顯可以通過手工做到這一點。重申我上面的問題:「Backbone是否允許一次設置/刪除屬性?」如果不是,那麼這個決定背後的理由是什麼? Derick Bailey創建了Backbone.Memento(https://github.com/derickbailey/backbone.memento)來處理屬性狀態,並且在Backbone上有關於與此場景密切相關的模型狀態(https://github.com/documentcloud/backbone/pull/2360,有點相關:https://github.com/documentcloud/backbone/issues/2316,高度相關:https://github.com/documentcloud/backbone/issues/2301)的幾個問題。

編輯2:我不是在尋找一個手卷的解決方案,我可以使它也或多或少我想要的(見上面的示例代碼)。 我正在尋找當前設計的理由,併爲此常見場景提供了一個乾淨的示例 - 一次設置和取消設置。

更新:https://github.com/documentcloud/backbone/issues/2301已經有一些關於此主題的討論。我已經提交了一個請求(https://github.com/documentcloud/backbone/pull/2368)來嘗試和鼓勵討論當前的實現。

謝謝大家誰發佈了答案!

回答

0

當前實現將允許您調用set()並傳入具有混合屬性的對象以進行設置和取消設置。要有效取消設置屬性,請爲其分配值undefined。密鑰將保留在模型的attributes哈希值中,但是無論何時將模型序列化爲JSON,undefined值都不會出現在序列化中(這是由於執行JSON.stringify())。它不會來自模型的屬性delete

JSON.stringify()除去系列化undefined值的行爲上MDN - JSON - stringify描述:

如果未定義,函數,或在轉換期間發生的XML值,則無論是省略(當它在一個對象被發現)或審查爲空(當它在數組中被發現時)。

我沒有對我的具體情況(BSON)使用JSON序列化,所以我最終不得不爲自己手動編寫一個解決方案。

我用拉取請求對GitHub進行了一次討論,最終決定保持API原樣。有關詳情,請參閱此拉取要求:https://github.com/documentcloud/backbone/pull/2368

再次感謝所有參與討論的人士,包括SO和GH。

0

像這樣的東西應該工作。

基本上遍歷所有屬性和unset無效的屬性(錯誤和非布爾值)。

/** Create a Model and set attributes */ 
var President = Backbone.Model.extend({}); 
var m = new President({first: 'Abraham', last: 'Lincoln', age: 90}); 

/** Blank out an attribute or two */ 
m.set({age: ''}); 

/** Loop through each attributes and unset falsy ones. 
    Also pass in silent = true so it doesn't trigger a change event 
     (in case you have listeners). 
*/ 
_.each(m.toJSON(), function(val, col){ 
    if (typeof val !=='boolean' && !val) { 
     m.unset(col, {silent: true}); 
    } 
}, this); 

/** Output the new Model */ 
console.log(m.toJSON()); 

OR

您可以創建一個新的Model只包含更改的屬性,如果你想要在這個方向走。

var President = Backbone.Model.extend({}); 
var m = new President({ 
    first: 'Abraham', last: 'Lincoln', age: 90, registered: true}); 

/** Blank out or change an attribute or two */ 
m.set({first: null}); 

/** Pass changed attributes to a new Model */ 
var t = new President(); 
if (!_.isEmpty(m.changedAttributes())) { 
    _.each(m.changedAttributes(), function(val, col) { 
     t.set(col, val); 
    }, this); 
} 
+0

謝謝你回答丹尼斯。你是在暗示我所尋找的東西根本不在設計中?我正在尋找一組更改(顯然,我在我的例子中傳遞給'set()'的對象就是這樣設置的)和一個修改後的結果對象(當然,我可以像你一樣手工完成)。也許我只是盯着這太久了。 =/ – clayzermk1 2013-03-12 01:04:34

+0

Backbone將整個'Model'發送到服務器是有原因的,因爲在大多數情況下,服務器期望這些屬性存在。除非你的服務器負責刪除未定義或空的屬性?除了我發佈的另一個選項是覆蓋'sync'功能並執行類似於我發佈的檢查。 – 2013-03-12 01:13:14

+0

謝謝。我使用MongoDB作爲我的服務器,雖然它會接受'未定義的值,但對我來說存儲它們並沒有什麼意義。我會看看'sync'覆蓋。乾杯! – clayzermk1 2013-03-12 17:05:41

1

有很多方法去皮膚這個!因此,我將專注於你的問題的一部分,你問:

有沒有一種方法,我可以一氣呵成unset()set()得到changedAttributes()

因爲我認爲這是去這裏的路。

Backbone.Model.unset()只是Backbone.Model.set()的別名。來源:

unset: function(attr, options) { 
    return this.set(attr, void 0, _.extend({}, options, {unset: true})); 
}, 

那麼,爲什麼不只是做m.set({"baz": "bar", "foo": void 0});?看到我從你的分手:http://jsfiddle.net/dimadima/Q8ZuV/。從那裏粘貼,結果將是

console.log(m.changedAttributes()); // { "baz": "bar", "foo": undefined } 
console.log(m.attributes); // // {foo: undefined, baz: "bar"}, unfortunately "foo"is not deleted 

所以m.attributes是有點過了,因爲你已經取消設置鍵一直沒有刪除,但你可以測試這一點。

無論如何,我建議略讀Backbone.Model.set()的來源來了解您的其他選項。我可以詳細說明你是否願意。

+0

謝謝你的回答dimadima。我在之前查看過'set()'的源代碼,並注意到'set()'中的'unset()'別名/'{unset:true}'標誌。 'void 0'在功能上評估爲'undefined',我認爲這與我在「我想要的」例子中所做的沒有什麼不同。我還需要完全刪除屬性(具有'undefined'值導致其他地方的問題)。 – clayzermk1 2013-03-12 01:18:21

+0

因此,在這種情況下,我會在'Backbone.Model.set'周圍定義我自己的'.set()',它只是一個'_.wrap'':調用'Backbone.Model.set'(參見「在超級「在http://backbonejs.org/#Model-extend),但後來也做了像丹尼斯建議的東西。然後你可以使用對象符號來調用你的'set()',它也會去除未定義的值。 – 2013-03-12 01:22:55

+0

簡而言之,如果您查看Backbone.model.set的源代碼,您會發現每次調用'.set'時都會重置this.changed,並且'.changedAttributes'依賴於這個。 changed'。所以你需要使用'.set'的對象形式,除非你想要設計一些方法來在調用之間積累變化時自己跟蹤變化。 – 2013-03-12 01:25:25

相關問題