Search code examples
backbone.jsbackbone-viewsbackbone-eventsbackbone-routingbackbone.js-collections

Backbonejs view binding conceptual feedback


I ran into this article (http://coenraets.org/blog/2012/01/backbone-js-lessons-learned-and-improved-sample-app/) and was wondering if the idea of binding and rendering views in the router after instantiating them is best practice. I have been binding my views and rendering them in my view definition.

Currently this is how I've been setting up and calling my views:

EmployeeView:

  EmployeeView = Backbone.View.extend({
  el: '#content',
  template:template,

  initialize: function () {

    this.collection.fetch({
      reset: true
    });

    this.collection.on('reset',this.render, this);
  },
  render: function(){
    this.el.innerHTML = Mustache.to_html(this.template, { employee_list: this.collection.toJSON()});
    console.log('render called');     
  }

My Router:

 employeeList: function () {
    var c = new EmployeeCollection
     new EmployeeView( {
       collection: c
     });
  }

It works fine. But according to the article a better practice is to do the following:

  EmployeeView = Backbone.View.extend({

  template:template,

  initialize: function () {

    this.collection.fetch({
      reset: true
    });

    this.collection.on('reset',this.render, this);
  },
  render: function(){
    this.el.innerHTML = Mustache.to_html(this.template, { employee_list: this.collection.toJSON()});
    console.log('render called');  
    return this;
  }

Router

  employeeList: function () {
    var c = new EmployeeCollection
    $('#content').html(new EmployeeView( {collection: c}).render().el);
  },

I like the solution in the article because it decouples the views from other DOM events as the article said and allows me to focus all my tweaking and customizing in one place, the router. But because I'm passing in a collection/model and need to fetch the data in my initialize my page renders twice. My questions are:

  1. Is this really best practice?
  2. How do I avoid calling the render twice if I want to use the suggested method?
  3. What if I have cases where I have some front end user interaction and then need to refresh the view collection/model? Would I have to do it in my view or could that happen in the router as well?

Solution

  • The view you have here, and the one in the article are totally different.

    In your example, the view is bound to an element in DOM (#content),
    which is not a good practice, especially for beginners and causes lots of bugs that we see here every day.

    For example if you create 2 instances of your view then event will starts firing multiples times and along with that all hell will break loose.


    The view in the article creates a new <div> element in memory per instance, which is a good practice.

    Now, to add this in DOM, newbies often do stuff like the following inside the view's render:

    $('#content').html(this.$el);
    

    This creates a global selector inside the view and makes it aware of the outer world which is not a good practice.

    The article probably (I didn't read it) address this is issue and presents and alternative of adding the view element to DOM from the router, which is a good practice in my opinion.


    To avoid rendering twice in the code from article you can just do:

    $('#content').html(new EmployeeView( {collection: c}).el);
    

    el being a live reference, it'll be updated when the fetch succeeds. .render().el is another common mis-understanding spread by all the existing blogs and tutorials.


    Side note: Since we are discussing best practices, omitting the semicolon and parenthesis as in var c = new EmployeeCollection is not a good practice either. Go with var c = new EmployeeCollection();