Backbone js - Uncaught type error: Cannot read pro

2019-05-23 02:07发布

问题:

I have the following a very simple ToDo List app using Backbone framework.

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>To do List</title>
    <style>
        .completed{
            text-decoration: line-through;
            color: #666;
        }
    </style>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.11.1.js"></script>
    <script type="text/javascript" src="js/underscore.js"></script>
    <script type="text/javascript" src="js/backbone.js"></script>
</head>
<body>

    <h1>My Todos</h1>
    <div id="tasks">

        <button class="add">Add Task</button>
        <button class="clear hide">Clear All</button>
        <ul></ul>           

    </div>

    <script id="taskTemplate" type="text/template">
        <span class="<%= completed ? 'completed' : 'incomplete' %>"><%= text %></span>
        <button class="complete"></button>
        <button class="delete"></button>
    </script>


    <script type="text/javascript">

        var Task = Backbone.Model.extend({
            defaults: {text: 'New task', completed: false}

        });

        var Tasks = Backbone.Collection.extend({
            model: Task,
            el: '#tasks',
            initialize: function(){
                this.collection = new Tasks;
                this.collection.on('add', this.appendNewTask, this);

                this.items = this.$el.children('ul');
            },

            add: function(){
                var text = prompt('What do you need to do?');
                var task = new Task({text: text});
                this.collection.add(task);
            },

            appendNewTask: function(task){
                var TasksView = new TasksView({model:task});
                this.items.append(TasksView.el);
            },

            completed: function(){
                return _.filter(this.models, function(model){
                    return model.get('completed');
                });
            }
        });

        var TasksView = Backbone.View.extend({
            tagName: 'li',
            el: '#tasks',
            template: _.template($('#taskTemplate').html()),

            initialize: function(){

                this.model.on('change', this.render, this);
                this.model.on('remove', this.unrender, this);
                this.render();
            },

            render: function(){
                var markup = this.template(this.model.toJSON());
                    this.$el.html(markup);
            },

            unrender: function(){
                this.$el.remove();
            },

            events: { 
                'click .add': 'add',
                'click .clear': 'clearCompleted',
                'click .delete': 'delete',
                'click .complete': 'updateStatus',
                'dblclick span': 'edit'
            },

            add: function(){
                    var text = prompt('What do you need to do?');
                    var task = new Task({text: text});
            },

            delete: function(){
                this.model.destroy();
                this.$el.remove();
            },

            updateStatus: function(){
                this.model.set('completed', !this.model.get('completed'));
            },

            edit: function(){
                var text = prompt('What should we change your task to?', this.model.get('text'))
                this.model.set('text',text);
            },

            clearCompleted: function(){
                var completedTasks = this.collection.completed();
                this.collection.remove(completedTasks);
            }

        });     

        new TasksView;

    </script>


    <!-- Template JS -->


</body>
</html>

When I visit and load the page, and I could see an uncaught javascript error in the console log.

Uncaught TypeError: Cannot read property 'on' of undefined on line 78.

Looking through the code at the indictaed line number, it pointed to this

var TasksView = Backbone.View.extend({
    //.....
    initialize: function(){

        this.model.on('change', this.render, this); // this is the line the console is complaining
        this.model.on('remove', this.unrender, this);
        this.render();
    },
    //.....
});

After staring this for the next couple of hours and analysed the code, I couldn't figure out why this model needs to be instantiated when no tasks have been added to the TaskView Objects yet. It should be initialized as soon as I add or remove a new Task item into the model. I don't quite understand how this would throw an exception.

BTW, I'm a very new to Backbone.js and Underscore.js. I'm just following an online tutorial to figure out how these frameworks work...

Thanks.

回答1:

You are not binding any model to your view. You can pass a model and/or collection when instantiating your new view. These properties will automatically be bound to this.model / this.collection of your view.

You are not passing a model so this.model is undefined in your view. You should pass the empty taskslist so that when it's populated later, the onchange will be triggered and your view will rerender.

new TasksView({model: new Tasks()});