By Richard Powell
By Richard Powell

Reputation: 1287

Backbone Views inside Collections

I am quite new to backbone and need use it to create a list item. Each list item has a model, and a view. Because its a list it seems like an ideal solution for collections, but I'm struggling to use them.

Here is the current version, which I would like to chaneg to use collections:

// The Model & view
var IntroModel = Backbone.Model.extend({});
var Introview = Backbone.View.extend({
    template: _.template( $('#taglist-intro').text() ),
    render: function() {
        console.log( this.model.attributes );
        this.$el.append( this.template( this.model.attributes ) );
    }
});

// We will store views in here
// Ideally this would be a collection
views = [];

// Get the data for that collection
$.getJSON( url, function( json ) {

    _.each( json, function( item ) {

        // Create each model & view, store views in the views array
        var model = new IntroModel( item );
        var view = new Introview({
        model : model
        })  
        views.push( view );

    })

})

// I can render a view like this
// But I'd rather it rendered the view when I add items to the collection
views[0].render()

So what i have works, but its not really doing it 'the backbone way'. Which seem a little pointless because:

  1. It would be better to use a collection, not an array
  2. It would be better that views render when items are added to the array
  3. Its not Backbone really is it..

Grateful for any pointers, if you cant provide specific code examples I'd still be very grateful to links & resources covering this issue.

Cheers,

Richard

Upvotes: 0

Views: 249

Answers (1)

Benjen
Benjen

Reputation: 2925

Your right that the current implementation is not the Backbone way. Most of what you are doing is handled directly by the collection object in backbone. In backbone collections are essentially just an array with additional methods attached to them. These methods are what gives collections their power. Backbone has a number of features including:

  • 'url' property: using this property the collection will automatically populate itself when you run the fetch method (e.g. myCollection.fetch() ).
  • You can bind a function to the 'reset' event of the collection. This event triggers when when you populate the collection. By including a call to your collection's render event your collection can automatically render the related view when the collection changes. There are also other collection events (e.g. 'add' new model, etc) which you can also attach functions to.
  • I find the Backbone documentation to be the best place to start. However a simple example is always useful. The following code shows how a simple collection can be defined, and how you would create two views (one view which creates a list, and another view which renders the item within the list). Note the use of the url property in the collection. Backbone uses this to retrieve the content of the collection when you run the fetch() method (See OrgListView object). Also note how the view's render method is bound to the collections 'reset' event, this ensures that the render event is called after populating the collection (See OrgsListView's initialize method).

            /**
         * Model
         */
        var Org = Backbone.Model.extend();
    
        /**
         * Collection
         */
        var Orgs = Backbone.Collection.extend({
            model: Org,
            url: '/orgs.json'
        });
    
        /**
         * View - Single Item in List
         */
        var OrgItemView = Backbone.View.extend({
            tagName: 'li',
            initialize: function() {
                _.bindAll(this, 'onClick', 'render');
                this.model = this.options.model;
                // Create base URI component for links on this page. (e.g. '/#orgs/ORG_NAME')
                this.baseUri = this.options.pageRootUri + '/' + encodeURIComponent(this.model.get('name'));
                // Create array for tracking subviews.
                /*var subViews = new Array();*/
            },
            events: {
                'click a.test': 'onClick'
            },
            onClick: function(event) {
                // Prevent default event from firing.
                event.preventDefault();
                if (typeof this.listContactsView === 'undefined') {
                    // Create collection of contacts.
                    var contacts = new ContactsByOrg({ url: '/orgs.json/' + encodeURIComponent(this.model.get('name')) });
                    this.listContactsView = new ListContactsView({ collection: contacts, baseUri: this.baseUri });
                    this.$el.append(this.listContactsView.render().el);
                }
                else {
                    // Close View.
                    this.listContactsView.close();
                    // Destroy property this.listContactsView.
                    delete this.listContactsView;
                }
            },
            onClose: function() {
    //      console.log('Closing OrgItemView');
            },
            render: function() {
                // TODO: set proper value for href. Currently using a dummy placeholder
                this.$el.html('<a class="test" href="' + this.baseUri + '">' + this.model.get('name') + '</a>');
                return this;
            }
        });
    
        /**
         * View - List of organizations
         */
        var OrgsListView = Backbone.View.extend({
            className: 'orgs-list',
            initialize: function() {
                console.log('OrgsListView');
                _.bindAll(this, 'render');
                this.pageRootUri = this.options.pageRootUri;
                this.collection = this.options.collection;
                // Bind render function to collection reset event.
                                    this.collection.on('reset', this.render);
                // Populate collection with values from server.
                this.collection.fetch();
            },
            onClose: function() {
                this.collection.off('reset', this.render);
    //      console.log('Closing OrgsListView');
            },
            render: function() {
                var self = this;
                this.$el.html('<ul></ul>');
                this.collection.each(function(org, index) {
                    var orgItemView = new OrgItemView({ model: org, pageRootUri: self.pageRootUri });
                    self.$('ul').append(orgItemView.render().el);
                });
                return this;
            }
        });
    

Upvotes: 1

Related Questions