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



我想把调用保存到我的服务器上,所以我目前正在使用带有patch选项的Model.save()并发送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)尝试并鼓励对当前实施情况进行讨论。

感谢所有发布答案的人!

有很多方法可以对这个进行剥皮!所以,我会把重点放在你的问题中你问的部分:

有没有办法让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()的来源,了解一下你的其他选择。如果你愿意,我可以详细说明。

这样的东西应该可以工作。

基本上循环遍历所有无效的属性和unset属性(falsy和非布尔值(。

/** 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);
}

当前的实现将允许您调用set()并传入一个具有混合属性的对象来设置和取消设置。要有效地取消设置属性,请为其指定值undefined。密钥将保留在模型的attributes散列中,但任何时候将模型序列化为JSON时,undefined值都不会出现在序列化中(这是由于JSON.stringify()的实现(。它不会delete模型中的属性。

在MDN-JSON-stringify:上描述了JSON.stringify()在序列化中移除undefined值的行为

如果在转换过程中遇到未定义、函数或XML值,则会将其忽略(当在对象中找到时(或删截为null(当在数组中发现时(。

我没有将JSON序列化用于我的特定案例(BSON(,所以我最终不得不为自己编写一个解决方案。

我在GitHub上讨论了一个pull请求,最后决定保持API的原样。有关详细信息,请参阅这个pull请求:https://github.com/documentcloud/backbone/pull/2368.

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

最新更新