javascriptjquerycanjscanjs-model

canJS: load model with associations, but only save the "base" model


Here is a demo, how to load a model with associations (in a single request) in canJS. I found it in the github repo of canJS, here, and actually I had to rewrite it a little bit to work (it was outdated), but it works now.

My problem is, that if I change a loaded contact (contact.attr('name', 'Tom');), and then I want to save it (contact.save();), then the contact, and also the contact's tasks will be posted via ajax to the server, to be saved. This is logical, because the tasks is an attribute of contact.

My problem is, that I only want the contact name, birthday and id to be posted when updating a record. I probably should override the makeRequest method, and remove tasks before posting to server, but I thinks there should be a more elegant solution.

Hopefully there are some canJS users here, who handled this situation already.


Solution

  • I was searching for a solution to this very problem myself, and I came up with my own one, inspired by trickey's suggestion on the JavascriptMVC forum in this post about modifying the serialize() model method to make it play more nicely with Rails.

    Like his suggestion, I created my own extension of the Model class, and I added an include field on my models where you can list the attributes you'd like to include (patterned after the Rails as_json method).

    can.Model("BaseModel", {}, {
      serialize: function() {
        var data, retval, serialized;
        data = {};
        retval = {};
        serialized = can.Model.prototype.serialize.call(this);
        //check if we're using the "include" fields or not
        if (typeof this.constructor.include !== 'undefined') {
          can.each(this.constructor.include, function(attr) {
            data[attr] = serialized[attr];
          });
        } else {
          data = serialized;
        }
        //wrap the return value in the model name for Rails purposes, e.g. {"event": {data}}
        retval[this.constructor._shortName] = data;
        return retval;
      }
    });
    

    Then in my model I use the "include" array to indicate which fields I want to include. By omitting "attendees" and "speakers", those associated models will not be packaged into my serialized JSON that gets sent back to the server.

    Event = BaseModel.extend('Event', {
      findAll: "GET /admin/events",
      findOne: "GET /admin/events/{id}",
      create: "POST /admin/events",
      update: "PUT /admin/events/{id}",
      destroy: "DELETE /admin/events/{id}",
      attributes: {
        attendees: 'Models.User.models',
        speakers: 'Models.User.models'
      },
      include: ['id', 'name', 'start_time', 'end_time']
    }, {});