Ross
Ross

Reputation: 1425

Backbone.js client model save issue on server-side error

Wondering if any one can help me out here.

I have a single page on the client side running backbone.js

The server is running socket.io.

I have replaced Backbones default sync with the backbone.iobind sync to support socket.io.

I have a backbone model that on save gets an error response from the server (which is planned), and triggers the save's error function, however my model on the client side still updates to the new value. I have included wait:true which from my understanding waits for a success response from the server before updating, however this does not happen for me.

Server side socket.io code

io.sockets.on('connection', function (socket) {
  socket.on('test:create', function (data, callback) {
    //err - used to fake an error for testing purposes
    var err = true;
    if (err) {
      // error
      callback('serv error');
    } else {
      // success
      // The callback accepts two parameters: error and model. If no error has occurred, send null for error.
      callback(null, data);
    }
  });

Client Side Code

  //HTML Code
  //<div id="callBtns">
    //<button id="runTest">Create</button>
  //</div>


  var CommunicationType = Backbone.Model.extend({
    defaults: {
      runTest: 'default'
    },
    urlRoot : '/test',
    validate: function(attrs) {
            //return 'error';    
    }
  }); 

  var BasicView = Backbone.View.extend({    
    el: $('#callBtns'), 

    initialize: function(){
      _.bindAll(this); 
    },

    events: {
    "click #runTest": "runTestFn"
    },

    runTestFn: function() {

      //causing the issue?
      communicationType.set({runTest: 'value from set'});     

      communicationType.save({},{
        success:function(model, serverResponse){
          console.log('Success');
          console.log(serverResponse);
        },
        error:function(model, serverResponse){
          console.log('Error');
          console.log(serverResponse);
          console.log(JSON.stringify(communicationType));
        },
        wait: true
      });
    }

  });

  var communicationType = new CommunicationType(); 
  var basicView = new BasicView();

I have noticed that this problem looks to be caused by the fact that I'm using set to update my model directly (communicationType.set({runTest: 'value from set'});)

If I don't set any values before saving, and pass the value in directly to save, like in the code below, it works correctly e.g.

communicationType.save({runTest: 'value from save'},{
            success:function(....

However this then means that I can't ever set any new values/update my model without it going via the save function :/

So my question is, how can I get this working correctly (get the client model to not update if there is a server-side error) while still being able to use set?

Any answers are really appreciated! Many Thanks

Upvotes: 1

Views: 497

Answers (2)

Andrey Kuzmin
Andrey Kuzmin

Reputation: 4479

You may use set with silent: true so it won't fire change event.

Upvotes: 0

Andrei Rosca
Andrei Rosca

Reputation: 1097

This can't be done because your model has no way of knowing if it's data is valid until a request is made (model.save in this case).

If you want to prevent Backbone.Model.set from working when you pass invalid values, you must update the model's "validate" function to check for errors.

Given the following validate function:

validate: function(attrs) {
    this.errors = [];
    if (attrs.runTest === 'wrong_value') {
      this.errors.push('error message');
    }
    return _.any(this.errors) ? this.errors : null;
}

on your CommunicationType model. Running this code in BasicView:

communicationType.set({runTest: 'wrong_value'});

will not change the model.

You can also use the validate function to check for server side errors by returning a errors array and checking for it:

validate: function(attrs) {
    this.errors = [];
    if (attrs.runTest === 'wrong_value') { // client side check
      this.errors.push('error message');
    }
    if (attrs.errors && attrs.errors.length > 0) { // server side check
        for (var key in attrs.errors) {
            this.errors.push(attrs.errors[key]);
        }
    }
    return _.any(this.errors) ? this.errors : null;
}

But this will only prevent changing the model when it is saved.

As a hack-ish alternative, you can add server requests inside the validate function that check for errors and prevent the "set" function from changing the model.

Upvotes: 2

Related Questions