2

no amount of Googling is managing to solve my confusion so I thought I'd ask the question on here.

I'm trying to save a model and make use of success/error callbacks. On the backbone documentation it states you save your model like so: model.save([attributes], [options]).

I cannot find anywhere on the documentation that tells you how to save the entire model (i.e. without specifying the attributes), but have come across this question where the second answer says to save the entire model you can do model.save({}, [options]).

However I am trying this to no avail. My code is below:

Backbone Model:

class Student extends Backbone.Model
  url: ->
    '/students' + (if @isNew() then '' else '/' + @id)

  validation:
    first_name:
      required: true
    last_name:
      required: true
    email:
      required: true
      pattern: 'email'

  schema: ->
    first_name:
      type: "Text"
      title: "First Name"
    last_name:
      type: "Text"
      title: "Last Name"
    email:
      type: "Text"
      title: "Email"

In my view I have the following function:

class Students extends CPP.Views.Base
  ...
  saveModel = ->
    console.log "model before", @model.validate()
    console.log "model attrs", @model.attributes
    @model.save {},
      wait: true
      success: (model, response) ->
        notify "success", "Updated Profile"
      error: (model, response) =>
        console.log "model after", @model.validate()
        console.log "model after is valid", @model.isValid()
        console.log "response", response
        notify "error", "Couldn't Update"

In the first console.log before the save I am told that the model is valid, via the means of an undefined response. If indeed I look at the model I can see that all three fields are filled in.

Similarly in the next two console logs in the error @model.validate() and @model.isValid() both return undefined and true respectively. However the response I get from trying to save the model is Object {first_name: "First name is required", last_name: "Last name is required", email: "Email is required"}

Finally in the console.log of the models attributes I get:

Object
  created_at: "2012-12-29 23:14:54"
  email: "email@email.com"
  first_name: "John"
  id: 2
  last_name: "Doe"
  type: "Student"
  updated_at: "2012-12-30 09:25:01"
  __proto__: Object

This leads me to believe that when I passed {} to my model it was actually trying to save the attributes as nil, else why else would it have errored?

Could someone kindly point out what I'm doing wrong? I'd rather not have to pass each attribute individually to the save!

Thanks in advance

Community
  • 1
  • 1
Sarah Tattersall
  • 1,275
  • 2
  • 21
  • 32

2 Answers2

1

Are you sure that the model's attributes have been set correctly before save? Even none of its attributes has been set, it may still pass validate(depending on how validate function is defined). Please try to print the model in the console to verify that. BTW, it's better to pass null instead of {} in save, this way the model's set method won't be invoked.

Updated:

According to the Backbone's source code, if passing null as the first argument of save, the model's attributes will be kept intact until the model has been saved on the server successfully. So the other possibility is that your server has succeeded in saving the model but returned a corrupted object, resulting in failure in model's set method. If you still cannot fix the problem, tracing the model.set method might help。

Hui Zheng
  • 10,084
  • 2
  • 35
  • 40
  • I've added a log of the model before into my question for you to see. Also changing {} to null hasn't seemed to help, but I will change it in my mode for better practices – Sarah Tattersall Dec 30 '12 at 10:38
  • @SarahTattersall I just updated my answer, please check if that helps. – Hui Zheng Dec 30 '12 at 11:00
  • It's a PUT request so I respond with head :no_content in my Rails controller. I've tried respond_with @student instead, but this doesn't appear to fix it either? I'm also a little dubious about responding with a student when it's a PUT request? – Sarah Tattersall Dec 30 '12 at 11:05
  • @SarahTattersall If you passed `null` or `{}` in `save`, it's server's responsibility to response an object with all updated attributes. After that, `Backbone` will call `model.parse(response, xhr)`(by default that just returns the response passed in) and use the returned value to set the model's attributes. Your problem may be fixed if the server returns an appropriate JSON object. BTW, it's perfectly reasonable to response a student object in a PUT request. – Hui Zheng Dec 30 '12 at 13:16
  • @SarahTattersall If you still cannot fix the problem, tracing the `model.set` method might help. – Hui Zheng Dec 30 '12 at 13:40
  • Even responding with a JSON version of the student doesn't seem to fix it. I'll investigate the set if possible – Sarah Tattersall Dec 30 '12 at 14:10
1

From the suggested answer by Hui Zheng I modified my controller in my server to return the student in JSON format.

However to find the real source of the problem I read the backbone documentation on saving, and found out that when wait: true is given as an option it performs the following:

if (!done && options.wait) {
        this.clear(silentOptions);
        this.set(current, silentOptions);
}

On further investigation into clear I found

clear: function(options) {
  var attrs = {};
  for (var key in this.attributes) attrs[key] = void 0;
  return this.set(attrs, _.extend({}, options, {unset: true}));
},

From this it looks as if every attribute is being cleared to then be reset. However on clearing my model the validations I wrote will fail (since first_name, last_name, email are required).

On the backbone.validation documentation we are told that we can use the parameter forceUpdate: true, so I have opted to use this when saving my model. I'm going to assume for now (although this might not be good practice) that the data from the server is correct since this has been validated too.

Therefore my final code is:

saveModel = ->
  @model.save {},
    wait: true
    forceUpdate: true
    success: (model, response) ->
      notify "success", "Updated Profile"
    error: (model, response) ->
      notify "error", "Couldn't Update"
Sarah Tattersall
  • 1,275
  • 2
  • 21
  • 32
  • I'm a little bit confused. I thought you were using Backbone 0.9.1 or 0.9.2, since method `isValid` has been removed in version 0.9.9. But now you are referencing the code snippet in Backbone 0.9.9. As far as I can tell, `clear` method isn't called in `save` method before 0.9.9. Actually `clear` method has even been removed in latest 0.9.9(https://raw.github.com/documentcloud/backbone/master/backbone.js). What's worse, method `isValid` revive in this latest version. So which version are you actually use? – Hui Zheng Dec 31 '12 at 01:55
  • 0.9.9, but now I'm confused. The documentation talks about clear, and indeed the changelog (on http://backbonejs.org/)does indeed say isValid has been removed, but as you pointed out the latest version still contains isValid. – Sarah Tattersall Jan 02 '13 at 14:51
  • As a quick update, I've realised my isValid is coming from backbone validation (https://github.com/thedersen/backbone.validation), rather than backbone itself – Sarah Tattersall Jan 02 '13 at 15:31