Search code examples
javascriptbackbone.jsampersand.js

Ampersand model not finding its url


I'm playing with Ampersand JS models. When I'm trying to initialize a new one,

z.add(this.state)
m = z.get(this.state.name)
m.sync()

or add one to a collection,

m = new ZModel(this.state)
m.sync()

I get an error A "url" property or function must be specified

According to the spec:

By default, url is constructed by sniffing for the model's urlRoot or the model's collection url, if present, then appending the idAttribute if the model has not yet been saved.

This is exactly the behaviour that I want, since I have a restful API to sync with. urlRoot is set, idAttribute is set too.

export default Model.extend({

  idAttribute: 'name',

  urlRoot: 'http://localhost:50000/locator/zones/',

  props: {
    name: 'string',
    type: 'string',
  },

  fetch () {
    Model.prototype.fetch.apply(this, arguments)
  }
})

A collection is defined, but I didn't pass it to the model, because I can't figure out from documentation how to do it. But supposedly it's not necessary as long as I have urlRoot defined. What to do here? I guess I could pass the collection to the model, or define the url programmatically somehow?

Update:

If I do this:

m.save({}, {
  success: function(model, response) {
    console.log('Rerender now')
  }.bind(this),
  error: function() {
    console.log('Error during model add')
  }
})

I don't get the same problem anymore, but instead of getting a POST, I get 2 requests with OPTIONS and PUT. If I omit the empty object {} argument, behavior doesn't change. Even though the spec says this:

book.save(); //=> triggers a POST via ampersand-sync with { "title": "The Rough Riders", "author": "Theodore Roosevelt" }

book.save({author: "Teddy"}); //=> triggers a PUT via ampersand-sync with { "title": "The Rough Riders", "author": "Teddy" }

I guess the documentation omits a lot of details. What's the secret to getting this to work?


Solution

  • Thanks to rick-butler and aaronmccall from Ampersand gitter chat for the answer:

    By default, if the model's ID attribute is populated the save method will PUT. POST vs PUT/PATCH is determined by whether the model has an ID by default. The actual logic is from the isNew() function in ampersand-state. You can overload this function to determine your own logic, or manually pass your preferred method in options.

    In cases where I don't have a distinctive ID, I like to add an attribute called fetched to the session of my derived ampersand-model. When I fetch/retrieve the model from the server I set this value. I use that for my isNew value, and I use it as the waitFor attribute for subviews.

    var Model = require('ampersand-model');
    
    module.exports = Model.extend({
      constructor: function(){
        options || (options = {});
        Model.apply(this, arguments);
        this.onSync = this.onSync.bind(this);
        this.on('sync', this.onSync);
      },
      isNew: function(){
        return this.fetched;
      },
      session:{
        fetched: ['boolean', true, false],
      },
      onSync: function (model, resp, options) {
        if (options.xhr.method == "GET") {
          this.fetched = true;
        }
      }
    });