backbone.stickit and html-form: How to save (patch

2019-09-12 06:11发布

问题:

tl;dr

How to use backbone.stickit with a html form to change an existing model fetched from the server and only PATCH the changed attributes (changed by user input within the html form) to the server?

/tl;dr

I'm using backbone.stickit in a backbone.js application to bind a model to a HTML-form which is part of a backbone view. This works fine so far, but it becomes a little bit complicated if I'm going to save the bound model. This is because I want to use the PATCH-method and only send the changed attributes to the server. I try to illustrate what I've done so far:

Fetching the model from Server

user = new User(); //instatiate a new user-model
user.fetch(); //fetching the model from the server

console.log(user.changedAttributes()); // Returns ALL attributes, because model was empty

The last line indicates my problem, because I thought I can used the changedAtrributes() method later to get the attributes which need a patch on the server. So I tried this workaround which I found here

user.fetch({
    success: function (model, response, options) {
        model.set({});
    }
});

user.changedAtrributes(); //Returns now "false"

Do stickit-bindings

Now I render my view and call the stickit() method on the view, to do the bindings:

//Bindings specified in the view:
[...]
bindings: {
  "#username" : "username"
  "#age"      : "age"
}

[...]

//within the render method of the view
this.stickit();

The bindings work fine and my user model gets updated, but changedAttributes() remain empty all the time.

Save the model to the server

If the user has made all required changes, the model should be saved to the server. I want to use the PATCH method and only send the changed attributes to the server.

user.save(null, {patch:true}); //PATCH method is used but ALL attributes are sent to the server

OR

user.save(user.changedAttributes(),{patch : true}); 

With the second approach there are different outcomes:

  1. if I didn't use the user.set({}) woraround, all attributes get PATCHED to the server
  2. if I use the user.set({}) woraround the return value of changedAttributes() is "false" and all attributes are PUT to the server
  3. if I call a user.set("age","123") before calling save(), then only the age attribute is PATCHED to the server

So outcome 3 is my desired behaviour, but there are 2 problems with this: First stickit doesn't seem to use the set() method on the model to update the attributes if they are changed within the html-form. And second, if you call set() with one attribute and afterwards with another, only the second attributes is returned by changedAttributes().

Maybe I just overseen something in the backbone or backbone.stickit docs, so I didn't get the desired behaviour working. Any ideas about that?

回答1:

NOTE: As found out the problem wasn't directly related to backbone.stickit, more to backbone itself.

Solved this problem on my own, maybe this helps someone who may stumble upon this question:

Backbone only keep track of unchanged attributes, but not of unsaved attributes. So with

model.changedAttributes();

you will only get the attributes of the model, which was changed since the last

model.set("some_attribute","some_value")

Finally I stumbled upon backbone.trackit which is a backbone.js plugin maintained by the creator of backbone.stickit. With this plugin you can track unsaved attributes (all attributes which have changed since the last model.save()) and then use them in the save-method of a model. Example (my usecase):

Backbone.View.extend({
  bindings: {
    "#name" : "name",
    "#age"  : "age"
  },

  initialize: function () {
    this.model = new User();
    this.model.fetch({
      success: function (model, response, options) {
        //this tells backbone.stickit to track unsaved attributes
        model.startTracking(); 
      }
    });
  },

  render: function () {
    this.$el.html(tmpl);
    this.stickit();
    return this;
  },

  onSaveUserToServer: function () {
    //first argument: only unsaved attributes, second argument: tell backbone to PATCH
    this.model.save(this.model.unsavedAttributes(), { patch: true });
  });

});