在Backbone.js中具有新值和未定义值的Model.set()

时间:2013-03-11 23:57:37

标签: backbone.js backbone-model

我想保存对我服务器的通话,因此我目前正在使用Model.save() patch选项并发送changedAttributes()

我想删除一个属性并添加一个新属性。 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),试图鼓励讨论当前的实施。

感谢所有发布回答的人!

3 个答案:

答案 0 :(得分: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()的来源,以了解您的其他选择。如果你愿意,我可以详细说明。

答案 1 :(得分: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());

如果您想转向那个方向,则可以创建一个仅包含已更改属性的新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);
}

答案 2 :(得分:0)

当前实现将允许您调用set()并传入具有混合属性的对象来设置和取消设置。要有效取消设置属性,请为其指定undefined的值。密钥将保留在模型的attributes哈希中,但只要模型序列化为JSON,undefined值就不会出现在序列化中(这是由于{{的实现) 1}})。它不会JSON.stringify()来自模型的属性。

MDN - JSON - stringify中描述了delete序列化中删除JSON.stringify()值的行为:

  

如果在转换过程中遇到未定义,函数或XML值,则将其省略(在对象中找到它)或删除为null(在数组中找到它时)。

我没有为我的特定情况(BSON)使用JSON序列化,所以我最终不得不为自己手动编写解决方案。

我用拉取请求对GitHub进行了讨论,最终决定保持API原样。有关详细信息,请参阅此拉取请求:https://github.com/documentcloud/backbone/pull/2368

再次感谢所有参与讨论的人,包括SO和GH。