user1551482
user1551482

Reputation: 529

how to render a collection using backbone?

I have this backbone that is working fine, i just want to render the collection is been fetched!

code:

var SearchView = Backbone.View.extend({
        events: {
            "keyup": "handleChange"
        },
        initialize: function(){
            this.collection.bind("reset", this.updateView, this);
        },
        render: function(){
          // this is where i need help 
          this.$el.next('#suggestions').append(// iterate over this.collection and apppend here) //

        },
        handleChange: function(){
            var term = this.$el.val();
            this.collection.search(term);
        },

        updateView: function() {

           this.render();
        }

    });

I just want to iterate over this.collection and display the "text" attribute thats inside each collection and append it to the ('#suggestions') el. thanks

Upvotes: 0

Views: 108

Answers (2)

jmk2142
jmk2142

Reputation: 8581

Focusing on your reset, render over collections issue, this is how I would do it. This way assumes that when you create your view, the $el is already present and you're passing it in through the View constructor so it's ready to go.

    var SearchView = Backbone.View.extend({
        template: _.template('<span><%= term %></span>');
        initialize: function(){
            this.collection.bind("reset", this.render, this);
        },
        render: function(){
            this.addAllTerms();
            return this;
        },
        addAllTerms: function() {
            var self = this;
            this.collection.each(function(model) {
                self.addTerm(model);
            });
        },
        addTerm: function(someModel) {
            this.$el.next('#suggestions').append(this.template(someModel.toJSON()));
        }
    });

It's a bit different from your approach in a few ways. First, we utilize Underscore's template function. This could be anything from span to li to div whatever. We use the <%= %> to indicate that we're going to interpolate some value (which will come from our model.term attribute).

Instead of going to the handler then render, I just bind the collection to render.

The render() assumes we're always going to refresh the whole thing, build from scratch. addAllTerms() simply cycles through the collection. You can use forEach() or just each() which is the same thing except forEach() is 3 characters too long for my lazy bum. ;-)

Finally, the addTerm() function takes a model and uses it for the value that it will append to the #suggestions. Basically, we're saying "append the template with interpolated value". We defined the template function up above as this View object property to clearly separate the template from data. Although you could have skipped this part and just append('<span>' + someModel.get('term') + '</span>') or what not. The _.template() uses our template, and also takes any sort of object with the property that lines up with the one in our template. In this case, 'term'.

It's just a different way to do what you're doing. I think this code is a little more manageable. For example, maybe you want to add a new term without refreshing the whole collection. The addTerm() function can stand on its own.

EDIT:

Not that important but something I utilize with templates that I found useful and I didn't see it when I first started out. When you're passing the model.toJSON() into the template function, we're essentially passing all the model attributes in. So if the model is like this:

defaults: {
    term: 'someTerm',
    timestamp: '12345'
}

In our previous example, the attribute timestamp is also passed in. It isn't used, only <%= term %> is used. But we could easily interpolate it as well by adding it to the template. What I want to get at is that you don't have to limit yourself to data from one model. A complex template might have data from several models.

I don't know if it's the best way, but what I do is have something like this:

makeHash: function() {
    var hash = {};
    hash.term = this.model.get('term');
    hash.category = anotherModel.get('category');

    var date = new Date();
    hash.dateAccessed = date.getTime();

    return hash;
}

So you can easily build your own custom hash to throw into a template, aggregating all the data you want to interpolate into a single object to be passed into a template.

// Instead of .toJSON() we just pass in the `makeHash()` function that returns
// a customized data object

this.$el.next('#suggestions').append(this.template(this.makeHash()));

You can also easily pass in whole objects.

makeHash: function() {
    var hash = {};
    hash.term = this.model.get('term');

    var animal = {
        name: 'aardvark',
        numLegs: 4
    };

    hash.animal = animal;

    return hash;
}

And pass this into our template that looks like this:

template: _.template('<span><%= term %></span>
    <span><%= animal.name %></span>
    <span><%= animal.numLegs %></span>')

I'm not sure if this is the best way but it helped me understand exactly what data is going into my templates. Maybe obvious but it wasn't for me when I was starting out.

Upvotes: 1

user1551482
user1551482

Reputation: 529

I found the solution to the problem, im gonna put it here for people that might want to know:

render: function(){
            this.collection.forEach(function(item){
                alert(item.get("text"));
            });
         }

Upvotes: 0

Related Questions