Search code examples
javascriptjquerybackbone.js

Page getting refreshed/reloaded many times


I am learning JavaScript and backbone.js and trying to develop one small web app. But the problem is that my page (graph) is getting reloaded many times (more than expected). So page automatically reloads many times (super fast) before the page gets stable and graph is displayed. When I say "Reload of browser" I mean to say the reload icon of google chrome refresh icon refreshes/ (does forward rotation, back rotation) many times and finally the data gets displayed.

Below is short version of what I have tried till now. Since I am in the learning phase, I might not have made correct coding standard. Bear with me on that.

So, I have to display one graph on the front page (Later I need to add more graphs on the same page). The data for the graph is coming from REST Service.

HTML:

I have one anchor, and one template to display the graph data.

 <div id ="chartAnchor1"></div>
<script id="myChart-template" type="text/template">
    <canvas id="lineChart"></canvas>
    </script>

ViewModel

This is for graph specific data:

  var firstSubViewModel = Backbone.View.extend({
    template: _.template($('#myChart-template').html()),
    events: {
  'click .Refresh': 'fetchModelData'
    },
      fetchModelData: function() {
      this.model.initialize();
    },
    render: function() 
    {
      $(this.el).html(this.template());
      var ctx = $(this.el).find('#lineChart')[0];
      var lineChart = new Chart(ctx, {
        type: 'line',
        data: {
          labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
          datasets: [{
            data: this.model.attributes.incThisYear
          }, {
            data: this.model.attributes.incLastYear
          }]
        },
                labelString: 'Income in $'
              }
            }]
          }
        }
      });
    },
   initialize: function() {
      _.bindAll(this, "fetchModelData");
      this.model.on('change', this.render, this);
    }
  });

  return firstSubViewModel;
});

Controller:

  var ch = new dashboardModel.chart({});
  if (// some condition) {
    var data = { // some data};
    ch.fetch(data)
  }     
  //Main Graph on Dashboard
  new firstSubViewModel({
    el: '#chartAnchor1',
    model: ch
  });

});

Model: I have one model class which gets the data from REST Service.

Question: Page is getting refreshed some 5-6 times and finally graph gets loaded perfectly.

FootNote: Is there any problem in my render function. Kindly guide.


Solution

  • You have bound the model's change event to the view's render method with this.model.on('change', this.render, this); this means for every change on model, view will re-render.

    And you're passing model attributes directly to some Chart() function. We don't know what's going on inside it, but chances are it's updating model attributes many times as part of chart drawing and obviously each change causes another render which invokes the Chart() method again, which invokes the render and hence creates a render loop that is likely as long as the number of changes made to model by Chart() function.

    Either remove this.model.on('change', this.render, this); or use model's toJSON() method to get a copy of it's data and pass it to chart() method for drawing. Use cases like this is the reason why toJSON() method exists.


    Another issue is handling the asynchronous fetch, I think this.model.on('change', this.render, this); was your attempt to handle this, but it's wrong. Either you should listen to events such as sync instead, like

    this.model.on('sync', this.render, this);
    

    or do something like:

    var ch = new dashboardModel.chart({});
    /* ---------------------------^ this is the actual model constructor?
     Then what's dashboardModel?! Bad naming ;) */
    
    ch.fetch({
      success: function(model, response) {
        new firstSubViewModel({
          el: '#chartAnchor1',
          model: ch
        });
      }
    });
    

    Apart from that you should not manually initialize models. It's a one time activity handled by Backbone.js and only one time initialization code should be added inside it. I can't even think of what you were trying to achieve by manually invoking initialize on an already initialized model.

    To fetch data from your persistence layer you should be using fetch() method.

    fetchModelData: function() {
      this.model.fetch({/* options */});
    }
    

    Your code in controller has many issues. It seems to try and fetch() a model with some data?, but fetch method only accepts options, mostly for customizing and handling the AJAX call like the ones I've used above (Maybe they were actually options but you named it data?! Ignore this one in that case).

    Also there's a view hanging around with no references for cleanup, a direct reference to DOM via el option which will cause issues if you create another instance of firstSubViewModel, use of on instead of listenTo etc all leading to classic Backbone.js bugs like memory leaks, issues with events and such.

    All those can't be handled in an answer so I suggest reading the docs properly and doing some research about common Backbone.js issues.


    Side note: According to common naming conversion, firstSubViewModel should be FirstSubViewModel because it's a constructor.