jesseniem
jesseniem

Reputation: 177

Lifecycle of a Backbone.js View during its creation

I'm new to backbone.js, and somewhat new to front-end work as well and haven't yet quite figured out how the lifecycle works.

We have a Django backend, which provides us with html templates, which we basically only use as frames. All the logic is handled in Backbone views.

The problem that I currently have is that I'm trying to draw a graph but the graphing function doesn't find the view based on the id as it doesn't exist during the render function, but I'm unaware of a way to achieve this at a later phase.

I've tried to create the view manually in Chrome console after the page has completely loaded and it works:

   var main = new MainView();
   main.showChart();

The View:

   var ChartView = Backbone.View.extend({

     title: 'Chart',

     initialize: function() {

    // This assures that this refers to this exact view in each function
    // (except within asynchronous functions such as jquery each)
    _.bindAll(this);

    // Saving parameters given by parent
    this.index = this.options.index;

    // Retrieve the template from server
    var template = _.template(getTemplate('chart.html'));

    // Compile the template with given parameters
    var compiledTemplate = template({'title': this.title});

    // Set the compiled template to this instance's el-parameter
    this.$el.append(compiledTemplate);

    // Rendering the view
    this.render();
 },

   render: function() {

    var self = this;

    // Draw the graph when the page is ready
    $(function(){
        self.drawGraph('chart1', this.line1Data);
    });

    // Render function should always return the instance
    return this;
},

drawGraph : function(chartId, lineData) {

    Morris.Line({
          element: chartId,
          data: [
            {y: '2012', a: 100},
            {y: '2011', a: 75},
            {y: '2010', a: 50},
            {y: '2009', a: 75},
            {y: '2008', a: 50},
            {y: '2007', a: 75},
            {y: '2006', a: 100}
          ],
          xkey: 'y',
          ykeys: ['a'],
          labels: ['Series A']
    });
},

});

Where it's created:

     var chartWidgetView = new WidgetView({'contentView': new ChartView()});
    this.initializeWidget(chartWidgetView);
    this.$el.append(chartWidgetView.el);

Could someone clarify to me:

  1. How does Backbone actually handle the creation of a view? What are the different stages?
  2. How should I handle this situation, e.g. at which point of my code would the element from the html template exist so that I could call the graphing function for it?
  3. Or is my architecture just fundamentally flawed? Should I try to do this in some completely different way?

Upvotes: 7

Views: 7038

Answers (3)

tsiki
tsiki

Reputation: 1799

You're creating the el but not attaching it to the DOM, and I suspect the chart searches only the DOM for the element to attach itself to. You need to attach it to DOM before calling the drawGraph.

As for the creation of the Backbone view, I think the best way to get an in-depth view is to simply read the annotated source code, which actually isn't that long.

Upvotes: 1

Derick Bailey
Derick Bailey

Reputation: 72868

FWIW, I think you're on the right track. But as your question notes, you've just got a few things out of order.

Strictly speaking, there isn't much of a life cycle built in to backbone views. When you instantiate one, it calls initialize. Other than that, it's up to you to decide what the lifecycle of the view will be. When it will be rendered. When the rendered el will be attached to the DOM, when it will be removed from the DOM, when it will be closed down and destroyed. That all depends on how you want to work with the view.

There are some things you do and add on to make this lifecycle easier to understand, of course. There are a few great frameworks that sit on top of backbone, for example. I recommend looking at LayoutManager, Thorax, Chaplin, and my own Marionette as starting points for this.

More to the point of your question, though, the plugin that you're describing is very likely dependent on the HTML elements being in the DOM before the plugin can run. This is common for visual plugins as they often need to capture location information, css information, positions of relative items, etc.

There are some very simple things you can do to facilitate these types of plugins and make this work. I've blogged about it a little, here: http://lostechies.com/derickbailey/2012/02/20/using-jquery-plugins-and-ui-controls-with-backbone/ - hopefully that will help get you down the path.

Specifically, what you'll want to look at is the idea of an onShow method. This isn't a method that Backbone understands on it's own. It's a concept that you'll have to add to your views and your apps. But the good news is that it's easy. Just add the method to your view, then call it at the right time:


var AnotherView = Backbone.View.extend({
  onShow: function(){
    this.$el.layout({ appDefaultStyles: true});
  }
});


// instantiate the view
var view = new AnotherView();

// render it
view.render();

// attach it to the DOM
$("#someDiv").html(view.el);

// now that it has been attached to the DOM
// you can call your DOM-dependent plugins
view.onShow();

Hope that helps.

Upvotes: 11

Chris Salzberg
Chris Salzberg

Reputation: 27384

Just glancing at this I think the problem is that the template is not returned from the server before you render it, hence it doesn't appear. You can get around this problem using jQuery's deferred object (see this ref). Try doing something like at the end of initialize:

this.deferredTemplate = $el.append(compiledTemplate);

this.deferredTemplate.done(function() {
  this.render();
});

See also Derick Bailey's article on deferreds in backbone.js: http://lostechies.com/derickbailey/2012/02/09/asynchronously-load-html-templates-for-backbone-views/

Upvotes: 1

Related Questions