3

我想将呼叫保存到我的服务器,所以我目前正在使用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),试图鼓励对当前实现的讨论。

感谢所有发布答案的人!

4

3 回答 3

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()以了解您的其他选择是什么。如果你愿意,我可以详细说明。

于 2013-03-12T01:04:49.650 回答
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);
}
于 2013-03-12T00:49:19.353 回答
0

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

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

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

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

我在 GitHub 上发起了一个 pull request 的讨论,最后决定保持 API 不变。有关详细信息,请参阅此拉取请求:https ://github.com/documentcloud/backbone/pull/2368 。

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

于 2013-04-05T18:12:11.057 回答