简体   繁体   中英

Organize a backbone.js collection?

How does one create a collection (in backbone.js) that is organized by an id. For example I would think it's quite common that collections need to be organized by date, category, keyword, type the list goes on. In my case I am trying to find an elegant way to organize players to team.

I don't think I need a plugin for this, my goal is just to organize the players to be grouped inside a div while using one clean json file so I can have a nice organized list. Again ideally it would be nice to use one json file for this, and structure the HTML similar to how the json itself is structured in terms of nesting, in my example league being the parent of everything, then team being the parent of the players .

I have gone through many backbone tutorials multiple times, and understand the flow and syntax pretty comfortably, but all tutorials work with one collection outputting a simple list in no specific order.

Basically I want to be able to create a clean json array like below. Then turn it into nice organized HTML.

{
  "league":
       [{
        "team":"Lakers",
        "players": [
                {
                    "name": "Kobe"
                },
                {
                    "name": "Steve"
                }
        ]
        },
        {
        "team":"Heat",
        "players": [
                {
                    "name": "LeBron"
                },
                {
                    "name": "Mario"
                }
        ]
        }]

}    

So this json structure is valid but it's not one I have used it's nested a little bit more, so accessing the models requires a little different technique, I am hoping to learn if this kind of array is ideal? Also if so how would I group say Kobe , Steve inside a div with perhaps the class name Lakers whilst obviously separating it from LeBron and Mario keeping those two inside a div with again perhaps a class of Heat .

Another example I could use would be like a actors to movies collection, again is the json format ideal (seems like it to me) here I obviously group actors to their respected movie? I would greatly appreciate some tips on building a clean view or views with a template or templates for outputting this nice and tidy.

{
  "movies":
       [{
        "title":"Prisoners",
        "actors": [
                {
                    "name": "Hugh Jackman"
                },
                {
                    "name": "Jake Gyllenhaal"
                }
        ]
        },
        {
        "title":"Elysium",
        "actors": [
                {
                    "name": "Matt Damon"
                },
                {
                    "name": "Jodie Foster"
                }
        ]
        }]

}

Again just working with this json data and backbone.js how do I create a maintainable view for this array?

Final Note: I was able to successfully group players to teams using two json files, and assigning a player and id that matched the team id, then looped the team collection with the player collection inside using where to organize it (I am trying to rethink this better). To me this is not taking advantage of backbone, it can get confusing and just seems wrong to me. So again I hope to improve my knowledge here and get better. I would immensely appreciate clear concise information, I really struggle to wrap my head around this topic :)

THANKS!!

Keep your JSON in a Backbone friendly structure, this will mean your models are easily organised once they are placed into the collection.

JSON example

[
    { league : 1, team : 'lakers', player : 'kobe' }
    { league : 1, team : 'lakers', player : 'steve' }
    // And so on
]

Consider that most backbone collections are built via a RESTful JSON api this would be easy to fetch directly into a collection and then sorted. The comparator() function on the collection is run each time a model is added to the collection, or when you ask for it to run.

Backbone collection example

var Players = Backbone.Collection.extend({

    initialize : function() {
        // Grab the JSON from the API
        // this.fetching is now a deferred object
        this.fetching = this.fetch();
    }

    // Comparator example one, as a string
    comparator : 'team',

    // Comparator example two, as a function
    comparator : function(model) {
        return model.get('team');
    }

});

The comparator as a function approach is obviously better suited to more complex sort algorithms, otherwise the comparator as a string approach would be better. Bear in mind that the function approach, though a string can be returned (such as above), -1 or 1 would be better return values to indicate it's sort position.

Comparator example with model comparison

comparator : function(modelA, modelB) {
     var teamA = modelA.get('team'),
         playerA = modelA.get('player'),
         teamB = modelB.get('team'),
         playerB = modelB.get('player');

     if (teamA < teamB) { // string comparison
         return 1;
     } else if (playerA < playerB} {
         return 1;
     } else {
         return -1;
     }
}

Now whenever a model is added to the collection it is sorted into it's correct location, if using the last example, by team and then by player name.

Simple view example using the collection

var ViewExample = Backbone.View.extend({

    el : "#example-container",

    render : function() {

        var self = this;

        // Use the deffered object to make sure models are
        // all available in the collection before we render
        this.collection.fetching.done(function() {

            self.collection.each(function(model) {

                self.$el.append('<p>' + model.get('player') + '</p>');

            });

        });

        return this;
    }
});

// Create the view and pass in the collection
// that will immediately fetch it's models
var view = new ViewExample({
    collection : new Players()
});

Code here is untested

Start by building a working model of your data. Your JSON suggests a hierarchy : a collection of teams that each have a collection of players. Here's a possible implementation :

var Player = Backbone.Model.extend();
var Players = Backbone.Collection.extend({
    model: Player
});
var Team = Backbone.Model.extend({
    constructor: function(data, opts) {
        // I like my subcollections as attributes of the model
        // and not on the settable properties
        this.players = new Players();

        Backbone.Model.call(this, data, _.extend(opts, {parse: true}));
    },
    parse: function(data) {
        // Players are handled in a subcollection
        if (_.isArray(data.players))
            this.players.reset(data.players);

        // They are removed from the model properties
        return _.omit(data, 'players');
    }
});
var Teams = Backbone.Collection.extend({
    model: Team,
    parse: function(resp) {
        return resp.league;
    }
});

Now you can create your root collection. Note that you could also fetch it instead of instantiating it with the data.

// teams list
var teams = new Teams(data, {parse: true});

// for example, to get all players in all teams
var allplayers = _.flatten(teams.map(function(team) {
    return team.players.models;
}));
console.log(_.invoke(allplayers, 'get', 'name'));

And a demo : http://jsfiddle.net/8VpFs/

Once you have your structure in place, you can worry about rendering it. Let's imagine you have this (Underscore) template

<ul>
  <% _(teams).each(function(team) { %>
      <li><strong><%= team.team %></strong>
        <ul>
          <% _(team.players).each(function(player) { %>
              <li><%= player.name %></li>
          <% }); %>
        </ul>
    </li>
  <% }); %>
</ul>

You can alter your Team model to output a serialized representation of you model:

var Team = Backbone.Model.extend({
    // ... as before

    toJSON: function() {
        var json = Backbone.Model.prototype.toJSON.call(this);
        json.players = this.players.toJSON();
        return json;
    }
});

and render your template

var tpl = _.template($('#tpl').html());
var html = tpl({
    teams: teams.toJSON()
})
$('body').append(html);

This usually would go into a view.

http://jsfiddle.net/8VpFs/1/

With a fetch

var teams = new Teams();
teams.fetch().then(function() {
    var tpl = _.template($('#tpl').html());
    var html = tpl({
        teams: teams.toJSON()
    });
    $('body').append(html);
});

http://jsfiddle.net/8VpFs/2/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM