【问题标题】:Backbone model virtual attributes?骨干模型虚拟属性?
【发布时间】:2014-05-11 19:59:20
【问题描述】:

我的骨干模型中需要虚拟属性,但骨干似乎不支持这一点,我将如何自己实现它。

虚拟属性是指仅在客户端维护的属性,不保存/同步到服务器。我想使用它们来存储仅与客户端视图渲染相关的状态信息,例如是否选择/检查模型。我将在视图的模板渲染中使用模型属性,但我不希望将这些值发送到服务器或保存在数据库中。

【问题讨论】:

标签: javascript backbone.js


【解决方案1】:

如果您只想在客户端将属性存储在模型上,为什么不直接使用: model.set({attribute: "client-side only"}) 代替: model.fetch()

为避免触发更改事件,您可以传入: model.set({silent:true}) 尽管文档不建议这样做。如果您感觉更冒险,您还可以覆盖 set 方法以使其真正保持沉默,如本答案中所述:

Truly change a model attribute silently in Backbone.js?

更新:

环顾四周: Backbone.js partial model updateExclude model properties when syncing (Backbone.js)

看起来您拥有的最简单的选项是: model.save(model.changedAttributes(), {patch: true});

或者如果您需要以一种良好的安静方式创建/更新这些对象,您可以像这样覆盖主干sync

 Backbone.Model.extend({

  // Overwrite save function
  save: function(attrs, options) {
   options || (options = {});

    // Filter the data to send to the server
    delete attrs.selected;
    delete attrs.dontSync;

    options.data = JSON.stringify(attrs);

    // Proxy the call to the original save function
    Backbone.Model.prototype.save.call(this, attrs, options);
  }
});

图片来源:Simon Boudrias

【讨论】:

  • 但是我需要从服务器获取模型,我只需要在某个地方/以某种方式存储临时状态属性。我正在使用 Marionette 和基于从服务器查询的集合结果的复合/集合视图。我担心如果我向这些模型添加属性,那么用户编辑并调用保存这些临时属性将被推送到服务器。
  • 感谢您的更新。我可以使用另一种选择。我仍在消化这些信息,但我认为覆盖保存功能可能比 toJSON 更有意义。这样,虚拟属性在视图/模板中始终可用,但不会发送到服务器。
【解决方案2】:

几个选项:

您可以创建一个通用主干模型并使用它来存储和侦听视图状态的属性。

StatefulView = Backbone.View.extend({
    initialize: function(options) {
        this.state = new Backbone.Model({
            selected: !!options.selected
        });
        this.listenTo(this.state, 'change:selected', this.renderSelectedState);
    },

    // ...
});

我建议不要在模型中存储任何与视图相关的属性,因为随着应用程序的增长,如果您在多个可选列表中显示相同的模型,它就不是一个可扩展的解决方案。但是...您可以覆盖模型的 toJSON 函数以删除您不想保留的属性:

ModelWithClientAttributes = Backbone.Model.extend({
    toJSON: function(withClientAttrs) {
        var json = Backbone.Model.prototype.toJSON.call(this);
        if (withClientAttrs) return json;
        return _(json).omit(this.clientAttrs);
    },

    // ...
});

// Extend from that base class when defining your model, and define the 'clientAttrs' property
Post = ModelWithClientAttributes.extend({
    clientAttrs: ['selected', 'checked'],
    // ...
});

// Then, in your view:
render: function() {
    this.$el.html(
        this.template(
            this.model.toJSON( true )
        )
    );
}

// Backbone.sync will call toJSON without arguments, so they will receive the filtered json representation
var post = new Post({
    selected: true,
    title: 'May Snow Storm',
    body: '...'
});
post.save();
console.log(post.toJSON()); // {"title": "May Snow Storm", "body": "..."}
console.log(post.toJSON(true)); // {"selected": true, "title": "May Snow Storm", "body": "..."}

可能最安全和最佳的解决方案是将您希望保留在服务器端代码中的属性简单地列入白名单。

【讨论】:

  • 您的 toJSON 建议听起来很有希望,我将不得不对其进行测试。虽然我同意不在模型中存储这些信息,但我正在使用 Marionette 并且集合视图从模型集合中呈现模板......我是否遗漏了什么,您还建议我如何解决这个问题?跨度>
  • @JonathanMiles 这是个好问题。在我最近的重构中,我创建了一个全局可访问的 App.Session 模型 (App.Session = new Backbone.Model()),我可以在该模型上获取和设置属性,并监听对这些属性的更改。 Then whenever your list is rendered, it can query the selection from this model and render appropriately (var selected = App.Session.get('inboxSelected'); ...) and when the selection changes, save it back (App.Session.set('inboxSelected', ...)).如果您想在页面加载时保持不变,您甚至可以在 App.Session 中添加逻辑以将信息保存在 cookie 中。
  • 谢谢@colllin。我实际上已经做了类似的事情,我已经在我的视图中覆盖了 templateHelpers 以添加全局可访问的 App.Settings、App.User 等,然后可以使用前缀 settings.attr、user.attr 在任何模板中访问它们。所以我明白你的建议。只是每次我想到这个问题时,我都会得出这样的结论,即这个值应该存储在模型中……但显然没有保存到服务器上。虚拟属性对我来说似乎是合乎逻辑的答案,这是骨干让我失望的第一件事。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-14
  • 2013-11-19
相关资源
最近更新 更多