Question

I am learning Backbone using a tutorial I found on YouTube. I have had trouble rendering Backbone Views. My html file looks like this and I get no error but the TweetsView does not show up:

This is a form,this part works like it is supposed to:

    <form id="new-tweet">
        <label>Author</label><input type="text" id="author-name" name="author-name"/>
        <label>Status</label><input type="text" id="status-update" name="status-update"/>
        <button>Post</button>
    </form> 
    <hr/>

Then I added the following div to put all the fake tweets in:

    <div id="tweets-container"></div>

I added the 3 dependency libraries:

    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <script type="text/javascript" src="scripts/lib/underscore.min.js"></script>
    <script type="text/javascript" src="scripts/lib/backbone.min.js"></script>

Tweet and TweetsList do work,I have used them with .toJSON(); to get the object returned successfully:

(function($){
            //use model to define a Bean
            var Tweet=Backbone.Model.extend({
            //pass some defaults in,just in case
                defaults:function()
                {
                    return {
                        //author:'jdorsey',
                        //status:'And on the eighth day god started learning web design using twitter like applications'
                        author:'',
                        status:''
                    }
                }
            });


            //a group of model-objects collected together
            var TweetsList=Backbone.Collection.extend({
            //all the models in the collection are of type Tweet
            model:Tweet
        });

These Views do not seem to be working:

        //In Backbone Views are more like Controllers...they allow us to render stuff on the screen...in order to create a view first create a template
        //this View represents a single tweet
        //creating a new view to use a template:    
        var TweetView=Backbone.View.extend({
            model:new Tweet(),
            tagName:'div'/*generating a new DOM element for each tweet*/,
            initialize:function(){
                this.template=_.template($('#tweet-template').html());
            },
            render:function(){
                //replace the place-holders in the template with the attributes from the model...and the content is going to be 
                //inserted into the element representing this template...
                this.$el.html(this.template(this.model.toJSON()));//bind the model to the template...
                return this;//default implementation        
            }
        });
        var tweets=new TweetsList();
        var TweetsView=Backbone.View.extend({
            model:tweets,
            el:$('#tweets-container'),
            initialize:function(){
                this.model.on('add',this.render,this);//pass the context in as the third argument...
            },
            render:function(){
                var self=this;
                _.each(this.model.toArray(),function(i){
                    self.$el.html('');
                    self.$el.append(new TweetView({model:tweet}).render().$el);
                    return this;
                });
            }
        }); 

This is executed once the page has finished loading:

        $(document).ready(function(){
            $('#new-tweet').submit(function(ev){
                var tweet=new Tweet({author:$('#author-name').val(),status:$('#status-update').val()});
                tweets.add(tweet);
                //.length gives you number of models
                //.models is an array that holds all the models
                //.first(n) only gives you the first n models
                //.toJSON converts it and gives a regular JS object as the output...to convert to JSON,use JSON.stringify 
                console.log(tweets.toJSON());
                return false;
            });
            var appView=new TweetsView();
        });
     })(jQuery);

This is the template I am using:

    <!-- Use the same names as inside the model in between the less than percent equal and percent greater than-->
    <!-- You can put the template anywhere in your HTML but using a script tag is a best practice,if you put it elsewhere,the browser might try to render it often and something stupid might happen -->
    <script type="text/template" id="tweet-template">
    <span class="author"><%= author %>:</span>
    <span class="status-update"><%= status %>:</span>
    </script>
Was it helpful?

Solution

I can see a few problems.

First, the TweetView has this line:

model:new Tweet(),

Remove that line. It creates a new instance of the Tweet model, which you don't want to do in the definition of the TweetView class. (You provide a model instance directly when you create each instance of TweetView later, in TweetsView.render.)

Second, in the _.each block in TweetsView.render, you have this line:

self.$el.html('');

Move that line before the _.each loop. That line clears the entire content of TweetsView, which you don't want to do inside the loop where you're rendering each row.

Third, in that same _.each block you're creating a new instance of TweetView and passing it a model option:

self.$el.append(new TweetView({model:tweet}).render().$el);

Change the value of the model option from tweet to i, which is the model instance for the current iteration of _.each. (I don't know what tweet is here; surprised you don't get a syntax error, but maybe it's defined in part of the file that's not shown.)

Finally, also in the _.each block, move the return this; line after the close of the _.each block, so it's the return statement for the render function itself. (No return needed in the _.each block.)

I think that render function should then look like this:

        render:function(){
            var self=this;
            self.$el.html('');
            _.each(this.model.toArray(),function(i){
                self.$el.append(new TweetView({model:tweet}).render().$el);
            });
            return this;
        }

See if it works after those changes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top