panosru
panosru

Reputation: 2959

Backbone.js Model different url for create and update?

lets say I have a Backbone Model and I create an instance of a model like this:

var User = Backbone.Model.extend({ ... });
var John = new User({ name : 'John', age : 33 });

I wonder if it is possible when I use John.save() to target /user/create when I use John.save() on second time (update/PUT) to target /user/update when I use John.fetch() to target /user/get and when I use John.remove() to target /user/remove

I know that I could define John.url each time before I trigger any method but I'm wondering if it could be happen automatically some how without overriding any Backbone method.

I know that I could use one url like /user/handle and handle the request based on request method (GET/POST/PUT/DELETE) but I'm just wondering if there is a way to have different url per action in Backbone.

Thanks!

Upvotes: 32

Views: 20272

Answers (5)

mlunoe
mlunoe

Reputation: 4490

I got inspired by this solution, where you just create your own ajax call for the methods that are not for fetching the model. Here is a trimmed down version of it:

var Backbone = require("backbone");
var $ = require("jquery");
var _ = require("underscore");

function _request(url, method, data, callback) {
  $.ajax({
    url: url,
    contentType: "application/json",
    dataType: "json",
    type: method,
    data:  JSON.stringify( data ),
    success: function (response) {
      if ( !response.error ) {
        if ( callback && _.isFunction(callback.success) ) {
          callback.success(response);
        }
      } else {
        if ( callback && _.isFunction(callback.error) ) {
          callback.error(response);
        }
      }
    },
    error: function(mod, response){
      if ( callback && _.isFunction(callback.error) ) {
        callback.error(response);
      }
    }
  });
}

var User = Backbone.Model.extend({

  initialize: function() {
    _.bindAll(this, "login", "logout", "signup");
  },

  login: function (data, callback) {
    _request("api/auth/login", "POST", data, callback);
  },

  logout: function (callback) {
    if (this.isLoggedIn()) {
      _request("api/auth/logout", "GET", null, callback);
    }
  },

  signup: function (data, callback) {
    _request(url, "POST", data, callback);
  },

  url: "api/auth/user"

});

module.exports = User;

And then you can use it like this:

var user = new User();

// user signup
user.signup(data, {
  success: function (response) {
    // signup success
  }
});

// user login
user.login(data, {
  success: function (response) {
    // login success
  }
});

// user logout
user.login({
  success: function (response) {
    // logout success
  }
});

// fetch user details
user.fetch({
  success: function () {
    // logged in, go to home
    window.location.hash = "";
  },
  error: function () {
    // logged out, go to signin
    window.location.hash = "signin";
  }
});

Upvotes: 0

Michael Kropat
Michael Kropat

Reputation: 15207

To abstract dzejkej's solution one level further, you might wrap the Backbone.sync function to query the model for method-specific URLs.

function setDefaultUrlOptionByMethod(syncFunc)
    return function sync (method, model, options) {
        options = options  || {};
        if (!options.url)
            options.url = _.result(model, method + 'Url'); // Let Backbone.sync handle model.url fallback value
        return syncFunc.call(this, method, model, options);
    }
}

Then you could define the model with:

var User = Backbone.Model.extend({
    sync: setDefaultUrlOptionByMethod(Backbone.sync),
    readUrl: '/user/get',
    createUrl: '/user/create',
    updateUrl: '/user/update',
    deleteUrl: '/user/delete'
});

Upvotes: 2

kubetz
kubetz

Reputation: 8556

Methods .fetch(), .save() and .destroy() on Backbone.Model are checking if the model has .sync() defined and if yes it will get called otherwise Backbone.sync() will get called (see the last lines of the linked source code).

So one of the solutions is to implement .sync() method.

Example:

var User = Backbone.Model.extend({

  // ...

  methodToURL: {
    'read': '/user/get',
    'create': '/user/create',
    'update': '/user/update',
    'delete': '/user/remove'
  },

  sync: function(method, model, options) {
    options = options || {};
    options.url = model.methodToURL[method.toLowerCase()];

    return Backbone.sync.apply(this, arguments);
  }
}

Upvotes: 80

dlamotte
dlamotte

Reputation: 6375

Are you dealing with a REST implementation that isn't to spec or needs some kind of workaround?

Instead, consider using the emulateHTTP option found here:

http://documentcloud.github.com/backbone/#Sync

Otherwise, you'll probably just need to override the default Backbone.sync method and you'll be good to go if you want to get real crazy with that... but I don't suggest that. It'd be best to just use a true RESTful interface.

Upvotes: 1

Andreas Köberle
Andreas Köberle

Reputation: 110892

No you can't do this by default with backbone. What you could to is to add to the model that will change the model url on every event the model trigger. But then you have always the problem that bckbone will use POST add the first time the model was saved and PUT for every call afterward. So you need to override the save() method or Backbone.sync as well.

After all it seems not a good idea to do this cause it break the REST pattern Backbone is build on.

Upvotes: 0

Related Questions