Question

I'am trying to run my js application in browser, but when i do, i get this error in console:

“Uncaught TypeError: undefined is not a function” app.js:18

I have read and tried other answers to similar problem, but with no success.

The code with the error line 18:

var AppView = Backbone.View.extend({

el: $("#todoapp"),

statsTemplate: _.template($('#stats-template').html()),

events: {
"keypress #new-todo":  "createOnEnter",
    "click #clear-completed": "clearCompleted",
    "click #toggle-all": "toggleAllComplete"
},

initialize: function() {

  this.input = this.$("#new-todo");
  this.allCheckbox = this.$("#toggle-all")[0];

  this.listenTo(Todos, 'add', this.addOne); //the line with the error
  this.listenTo(Todos, 'reset', this.addAll);
  this.listenTo(Todos, 'all', this.render);

  this.footer = this.$('footer');
  this.main = $('#main');

      Todos.fetch();
},

render: function() {
      var done = Todos.done().length;
      var remaining = Todos.remaining().length;

      if (Todos.length) {
        this.main.show();
        this.footer.show();
        this.footer.html(this.statsTemplate({done: done, remaining: remaining}));
      } else {
        this.main.hide();
        this.footer.hide();
      }

      this.allCheckbox.checked = !remaining;
    },

addOne: function(todo) {
      var view = new TodoView({model: todo});
      this.$("#todo-list").append(view.render().el);
    },

addAll: function() {
      Todos.each(this.addOne, this);
    },

createOnEnter: function(e) {
      if (e.keyCode != 13) return;
      if (!this.input.val()) return;

      Todos.create({title: this.input.val()});
      this.input.val('');
    },

clearCompleted: function() {
      _.invoke(Todos.done(), 'destroy');
      return false;
    },

toggleAllComplete: function () {
      var done = this.allCheckbox.checked;
      Todos.each(function (todo) { todo.save({'done': done}); });
    }

});

Here is my collection.js code:

var TodoList = Backbone.Collection.extend({
model: Todo,
localStorage: new Backbone.LocalStorage("todos-backbone"),

done: function(){
return this.where({done: true});
},

remaining: function(){
return this.without.apply(this, this.done());
},

nextOrder: function(){
if (!this.length) return 1;
return this.last().get('order')+1;
},

comparator: 'order'

});

var Todos = new TodoList();
Was it helpful?

Solution

You need to upgrade your Backbone version if you want to use listenTo. From the fine ChangeLog:

0.9.9Dec. 13, 2012

  • Added listenTo and stopListening to Events. They can be used as inversion-of-control flavors of on and off, for convenient unbinding of all events an object is currently listening to. view.remove() automatically calls view.stopListening().

The listenTo method showed up in Backbone 0.9.9 but you're using 0.9.1.

Read the ChangeLog and upgrade your Backbone version.

OTHER TIPS

this.listenTo(Todos, 'add', this.addOne);

You are using the Todos as an object that references to collection but the object for the collection in neither passed nor created in the view.

If you want to pass

var mycollection = new Todos();
new AppView({ myCollection: mycollection});

Since we are passing a key value we can fetch it back in view like

this.myCollection.add(model) or
this.myCollection.reset(); //to reset the collection

If you want to create a object for collection ,you can directly like the same way

var mycollection = new Todos();

Collections callback functions can be defined inside collection itself.This will help in reusing the function over multiple views .

Had the same problem and after a few looks I realized that I misspelled the name of the element when i was initializing:

  this.allCheckbox = this.$("#toggle-all")[0];

So, check the name of the elements in your html

Quite sure your problem is not on that line, but rather:

this.input = this.$("#new-todo");
this.allCheckbox = this.$("#toggle-all")[0];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top