关于 javascript:更新一个模型但保留整个 Backbone 集合

Update a model but persist the whole Backbone collection

我的 API 响应:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
  firstName: '',
  lastName: '',
  notifications: [
    {
      category: '1',
      subcategory: '',
      source: '',
      optInDate: '',
      optOutDate: '',
      active: ''
    },
    {
      category: '2',
      subcategory: '',
      source: '',
      optInDate: '',
      optOutDate: '',
      active: ''
    }
  ]
}

我对响应的对应Backbone实现是:

1
2
3
4
5
6
7
8
9
10
11
12
var Notification = Backbone.Model.extend({
  initialize: function (args) {  },
});

var Notifications = Backbone.Collection.extend({
  model: Notification,
  url: '/xyz/abc',

  parse: function (data) {
    return data.notifications;
  },
});

我只想用 active: true 更新"类别 1"模型(这总是正确的),但将整个集合保存在我的请求有效负载中,而不仅仅是更改后的模型。我们如何通过Backbone 实现这一目标?

我已尝试获取该特定模型并执行 model.set({active: true}) 并调用 model.save() 但这只会将该模型发送到服务器。

我需要将整个响应与更新的模型一起持久化回服务器。

编辑:

在@Khang 在下面的回答中的帮助下,我能够做到这一点。但是我忘了提到我还需要将其他属性与集合一起发送,即上述响应中的 firstNamelastName

我正在重写 parse 方法以将它们包括在内,如下所示:

1
2
3
4
5
parse: function(data) {
  this.firstName = data.firstName;
  this.lastName = data.lastName;
  return data.notifications;
}

当我调用 Backbone.sync 时,它仍然只发送有效负载中的集合。那是因为我在 parse 方法中只返回来自数据对象的通知吗?


我会说不要像 Khang 建议的那样使用 sync 并且重写 Backbone.sync 函数应该非常小心,因为它由每个模型和集合共享。

虽然它现在适用于您的用例,但从长远来看,它更像是一个补丁。

the API expects the whole DTO to be sent back in the payload to maintain RESTFul

如果 API 是真正的 RESTful,它将提供一个端点来单独管理通知。

作为未来读者的参考,制作一个端点来单独管理模型。这是创建 REST API 时要走的路。我在另一个答案中考虑了 Backbone 这种技术的优缺点。

API is not open to changing their implementation

既然不能,这意味着 API 响应代表的是一个模型,而不是一个集合。 Backbone 提供了基于 RESTful API 的预定义行为,其中一个对象应该是一个模型,而一个对象数组应该是一个集合(例外是在接收带有页面计数等集合的元数据时)。由于这个原因,一个 save 函数,它不应该一次全部保存。

在你的情况下,看起来用户模型可能会完成这项工作。

请注意,有很多替代方法可以在模型中实现集合。这是一个简单的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var UserModel = Backbone.Model.extend({
    urlRoot: '/xyz/abc',
    initialize: function(attrs, options) {
        // init a collection within the model
        this.notifications = new Notifications((attrs || {}).notifications);
        // update the collection whenever the model is synced.
        this.listenTo(this, 'sync', this.onSync());
    },

    // Useful abstraction that doesn't interfere with the normal use-case
    saveNotifications: function() {
        return this.save({
            notifications: this.notifications.toJSON()
        }, { patch: true });
    },

    onSync: function() {
        this.notifications.reset(this.get('notifications'));
    }
});

您可以像使用其他任何模型一样使用该模型

1
2
var user = new UserModel({ id: 'user-id-1' });
user.fetch();

当您需要通知时,它们已经在一个方便的集合中可用。

1
2
3
4
5
var notif = user.notifications.findWhere({ category: '1' });
if (notif) {
    notif.set({ active: true });
    user.saveNotifications();
}

用户模型上的 sync 和通知集合上的 reset 等主干事件将正确触发。

在处理大量数据时,这种技术也有缺点,无论如何,一次保存所有集合应该会暴露出来。延迟加载和分页是在这种情况下提高性能的方法,显然,遵循 REST 最佳实践会有很大帮助。

在我们公司,我们的 API 深受"构建你不会讨厌的 API"的启发,我很高兴我们正在使用这本书。


我不确定你想做什么,我觉得它不正确。如果只更改一个模型,为什么需要将整个集合发送到服务器?未更改的模型不是已经在服务器上吗?我认为您应该重新考虑这种方法。

无论如何,如果你真的需要它,这将实现你想要的:

1
Backbone.sync("update", Notifications);

这里有更多文档

更新:

如果每次集合为 sync 时您有许多额外的数据要发送,您可以重写 sync 方法以使集合具有自定义同步行为。否则,您可以在需要时使用 options.attrs 来获取自定义数据:

1
2
3
4
5
6
7
Backbone.sync("update", Notifications, {
    attrs: {
        data: Notifications.toJSON(),
        firstName: 'foo',
        lastName: 'bar',
    }
});

要了解为什么会这样,我建议您阅读 Backbone 源代码 Backbone.sync