Om3ga
Om3ga

Reputation: 32823

having issue with backbonejs router

Scenario

I am working on backbone app. What is happening right now is when user clicks edit link on page then it should show a form. I am trying to implement this using backbone routers rather than events. With events object it works perfectly fine. To use routers, I am using global events.

Problem

The problem is that when user clicks on edit link, it shows me following error in console

Uncaught TypeError: Object 10 has no method 'toJSON'             views.js:57

This error is because on line 57 in views.js, I am using this.model.toJSON() whereas I am not passing model via router. I don't know how pass model through router

Here is my router. Note: All of the following codes are in separate files

App.Router = Backbone.Router.extend({
    routes: {
        'contacts/:id/edit': 'editContact'
    },

    editContact: function (id) {
        console.log('yahhhhh');
        vent.trigger('contact:edit', id);
    }
});

In above router I am triggering an event inside editContact function. Then I am listening to above event in following initialize function.

App.Views.App = Backbone.View.extend({
    initialize: function () {
        vent.on('contact:edit', this.editContact, this);
    },

    editContact: function (contact) {
        var editContactView = new App.Views.EditContact({ model: contact });
        $('#editContact').html(editContactView.render().el);
    }
});

Now in above after listening to event in initialize function, I am calling editContact function and I am also passing model using this keyword. Inside editContact function, I am creating an instance of EditContact, view which is following, and then rendering a form which needs to be shown.

App.Views.EditContact = Backbone.View.extend({
    template: template('editContactTemplate'),

    render: function () {
        var html = this.template(this.model.toJSON());  //<--- this is line 57
        this.$el.html(html);
        return this;
    }
});

After doing all of the above, the form is not shown and I am getting above mentioned error.

Question

How do I pass model to render function inside EditContact via router so that it starts working?

UPDATE

Here is my model

App.Models.Contact = Backbone.Model.extend({
    urlRoot : '/contacts'
});

Upvotes: 1

Views: 165

Answers (1)

jevakallio
jevakallio

Reputation: 35890

In your editContact method the argument contact refers to the id you pass onwards from the router. When you initialize a new view with new App.Views.EditContact({ model: contact }) the model of the view will, expectedly, be the id.

You need to map the id into a model instance. IMHO the correct place to do this is in the router:

editContact: function (id) {
    var contact = new App.Models.Contact({id:id});
    vent.trigger('contact:edit', contact);
}

Notice that at this point the model will only have the id property set. If you need to populate the model properties for editing, you should fetch the model from the server, and only then trigger the event:

editContact: function (id) {
    var contact = new App.Models.Contact({id:id});
    contact.fetch().done(function() {
      vent.trigger('contact:edit', contact);
    });
}

Edit based on comments: Generally speaking you shouldn't pass anything to the router. The router should be a starting point for every new request (url change). If you want to hold some state between page changes, you should store the data on the router level, and pass the models and collections "down" from the view.

In a simplified scenario this would mean initializing and storing a reference to the collection in the router. Something like:

var Router = Backbone.Router.extend({
  initialize: function() {
    this.contactCollection = new App.Collections.Contacts();
  },

  editContact: function (id) {
    id = parseInt(id, 10);
    if(_.isNaN(id)) {
      //error...
    }
    //try to get a model from the collection
    var contact = this.contactCollection.get(id);

    //if not found, create, add and fetch
    if(!contact) {
      contact = new App.Models.Contact({id:id});
      this.contactCollection.add(contact);
      contact.fetch().done(function() {
        vent.trigger('contact:edit', contact);
      });
    } else {
      vent.trigger('contact:edit', contact);
    }
  }
});

Please note that this is just example code, and not necessarily how you should implement it, line by line. You should consider whether it's OK to display a potentially stale model in the view, or whether you should always fetch it from the server. In practice you might also abstract the collection state in a nice class, instead of handling it directly in the router.

Hope this answers your questions.

Upvotes: 1

Related Questions