Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

backbone.js - getting extra data along with the request

I have a collection which holds some of the users. Some information that is needed is how many total there are, how many pages, etc. How do I pass these back to the client? Or do they have to come from a separate view in which case I will need more than one ajax call? I'd like to have the collection fetch() and also receive some of this "meta data". What's a good way for handling this?

like image 309
Matthew Avatar asked Apr 19 '11 03:04

Matthew


People also ask

Is Backbone JS still used?

Backbone. Backbone has been around for a long time, but it's still under steady and regular development. It's a good choice if you want a flexible JavaScript framework with a simple model for representing data and getting it into views.

How does Backbone JS work?

Backbone. js gives structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a RESTful JSON interface.

Is Backbone JS frontend or backend?

Front-End MVC frameworks (Backbone, Angular, etc) all rely on a backend service to provide the data that, say Backbone, would then use as its model. You could have an entire MVC pattern on the backend that accepts requests and spits out some JSON for a frontend MVC framework to use.


2 Answers

Generally, you need to handle this in the collection class' parse method. Its responsibility is to take the response and return back an array of model attributes. However, you could do more than that if you wished if you didn't mind the parse method having this additional responsibility.

UserList = Backbone.Collection.extend({

    model: User,

    url: '/users',

    parse: function(data) {
        if (!data) {
            this.registered_users = 0;
            return [];
        }
        this.registered_users = data.registered_users;
        var users = _(data.users).map(
            function(user_data) {
                var user = {};
                user['name'] = user_data.name;
                return user;
            }
        );
        return users;
    }
});

So in the trivial example above, presume the server returns a response which contains a count of registered users and and an array of user attributes. You would both parse through and return the user attributes and you would pick off the registered user count and just set it as a variable on the model.
The parse method would get called as part of a fetch. So no need to modify the fetch, just use the built-in hook method that you have.

Purists would say that you are giving the parse method a secondary responsibility which might surprise some people (e.g. returning something and modifying model state). However, I think this is okay.

like image 126
Bill Eisenhauer Avatar answered Oct 14 '22 19:10

Bill Eisenhauer


One way to do this is to override the Collection::fetch() method so that it parses this metadata out of the response. You could have your backend return a response like this:

{
    "collection": [
        { ... model 1 ... },
        { ... model 2 ... },
        ...
    ],
    "total_rows": 98765,
    "pages":      43
}

In your fetch method which overrides the original Backbone.Collection::fetch() method, you can handle each property of the object separately. Here's you could do the override with a slightly modified fetch method:

_.extend(Backbone.Collection.prototype, {
  fetch : function(options) {
    options || (options = {});
    var collection = this;
    var success = options.success;
    options.success = function(resp) {
      // Capture metadata
      if (resp.total_rows) collection.total_rows = resp.total_rows;
      if (resp.pages)      collection.pages      = resp.pages;

      // Capture actual model data
      collection[options.add ? 'add' : 'refresh'](
        collection.parse(resp.collection), options);

      // Call success callback if necessary
      if (success) success(collection, resp);
    };
    options.error = wrapError(options.error, collection, options);
    (this.sync || Backbone.sync).call(this, 'read', this, options);
    return this;
});

Note that this approach using _.extend will affect all your classes which extend Backbone.Collection.

This way, you don't have to make 2 separate calls to the backend.

like image 24
Sam Avatar answered Oct 14 '22 20:10

Sam