简体   繁体   中英

Update a model but persist the whole Backbone collection

My API response:

{
  firstName: '',
  lastName: '',
  notifications: [
    {
      category: '1',
      subcategory: '',
      source: '',
      optInDate: '',
      optOutDate: '',
      active: ''
    },
    {
      category: '2',
      subcategory: '',
      source: '',
      optInDate: '',
      optOutDate: '',
      active: ''
    }
  ]
}

My corresponding Backbone implementation for the response is:

var Notification = Backbone.Model.extend({
  initialize: function (args) {  },
});

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

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

I want to only update the "category 1" model (this is always true) with say active: true but persist the whole collection in my request payload and not just the changed model. How do we accomplish this with backbone?

I have tried getting that particular model and doing a model.set({active: true}) and calling model.save() but that sends only that model to the server.

I need the whole response to be persisted back to the server along with the updated model.

EDIT:

I was able to achieve this with @Khang's help in his answer below. However I forgot to mention that I need to send other attributes too along with the collection ie, firstName , lastName from the above response.

I'm overriding the parse method to include them as below:

parse: function(data) { 
  this.firstName = data.firstName; 
  this.lastName = data.lastName; 
  return data.notifications; 
} 

When I call Backbone.sync , it still sends just the collection in the payload. Is that because I am returning just the notifications from data object in the parse method?

I'd say don't use sync like Khang suggests and overriding the Backbone.sync function should be done with extreme caution since it is shared by every models and collections.

While it works for your use-case right now, it's more of a patch in the long run.


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

If the API was truly RESTful, it would provide an endpoint to manage the notifications individually.

As a reference to future readers, make an endpoint to manage models individually. This is the way to go when creating a REST API. I go over the pros and cons of this technique with Backbone in mind in another answer .

API is not open to changing their implementation

Since you can't, that means the API response is representing a model, not a collection. Backbone offers predefined behaviours based on a RESTful API where an object should be a model, and an array of objects should be a collection (exception being when receiving metadata with a collection like page count, etc.) A Backbone Collection doesn't have a save function for this reason, it shouldn't be saved all at once.

In your case, it looks like a user model would probably do the job.

Note that there's a lot of alternatives to implement a collection within a model . Here's one quick example.

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'));
    }
});

You can use the model like any other

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

And when you need the notifications, they're already available within a handy collection.

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

Backbone events like sync on the user model and reset on the notifications collection will trigger correctly.

When dealing with lots of data, there are downsides to this technique, which saving the collection all at once should bring to the light anyway. Lazy loading and paginating are ways to improve performance in this situation, and obviously, following REST best practices will help a lot.

At our company, our APIs were heavily inspired by "Build APIs you won't hate" and I'm glad that we're using this book.

I am not sure what you are trying to do, it does not seem right to me. If only one model change, why do you need to send the whole collection to server? Aren't the unchanged models already on server? I think you should reconsider this approach.

Anyway if you really need it, this would achieve what you want:

Backbone.sync("update", Notifications);

More docs here

Update :

If you have many extra data to send along every times the collection is sync , you can override sync method to have custom sync behavior for the collection. Otherwise you can use options.attrs to have custom data when you need it:

Backbone.sync("update", Notifications, {
    attrs: {
        data: Notifications.toJSON(),
        firstName: 'foo',
        lastName: 'bar',
    }
});

To understand why this works, I suggest you reading the Backbone source code Backbone.sync

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM