Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone partial view not rendering the latest model

I am relatively new to Backbone and I am running into this problem.

I am using Backbone with DustJS

My template looks something like this - index.dust

<div id="parentView">
  <div class="section">
    {>"app/inc/responseMessage" /}
    <div class="userDetails">
      {! A button to get user details !}
    </div>
  </div>
</div>

This is my partial below - responseMessage.dust

<div id="responseMessage">
  {@eq key="{data.success}" value="true"}
    <div class="alert alert-success success" role="alert">success</div>
  {/eq}
</div>

My JS looks like this

initialize: function() {
    this.responseMessageView = this.responseMessageView ||
        new ResponseMessageView({
            model: new Backbone.Model()
        }); // View is for the partial
    this.model = this.model || new Backbone.Model(); //View for the whole page
},

Below function is called when an event occurs and it does a POST and returns successfully.

primaryViewEventTrigger: function(event){
  //Button click on `index.dust` triggers this event and does a POST event to the backend

  this.listenToOnce(this.model, 'sync', this.primaryViewSuccess);//this will render the whole view. 
  this.listenToOnce(this.model, 'error', this.primaryViewError);
  this.model.save({data: {x:'123'}});
}

responseViewEventTrigger: function(event){
  //Button click on `responseMessage.dust` triggers this event and does a POST event to the backend

  this.listenToOnce(this.responseMessageView.model, 'sync', this.responseViewSuccess);//it should only render the partial view - responseMessage.dust
  this.listenToOnce(this.responseMessageView.model, 'error', this.primaryViewError);
  this.responseMessageView.model.save({data: {x:'123'}});
}
primaryViewSuccess: function(model,response){
    this.model.set('data', response.data);
    this.render();
}
responseViewSuccess: function(model,response){
    this.responseMessageView.model.set('data', response.data);
    console.log(this.responseMessageView.model);
    this.responseMessageView.render(); // Does not work in some cases
}

My implementations of the callback function

exports.sendEmail = function sendEmail(req, res){
  req.model.data.success = true;
  responseRender.handleResponse(null, req, res);
};

this.model belongs to the model of the whole page. Whereas this.responseMessageView.model is the model for the partial.

Question: This works perfectly fine in most of the cases. There is one case where it does not render the partial with the latest model values. When I click on the button on index.dust and primaryViewSuccess is executed. After which I click on another button and trigger responseViewEventTrigger. It does the POST successfully and it comes to responseViewSuccess and stores it in the model too. But it does not show it on the frontend. data.success is still not true whereas console.log(this.responseMessageView.model) show that attributes->data->success = true

But the same behavior when I refresh the page it all works perfect. Its just that when primaryViewSuccess is called and then responseViewSuccess its not taking the latest model changes. In other words model is being updated but the DOM remains the same.

What am I missing here? Thanks for your time!

like image 540
suprita shankar Avatar asked Oct 23 '15 20:10

suprita shankar


1 Answers

You're hitting the classic Backbone render-with-subviews gotcha: When you render the index view, you are replacing all of the DOM nodes. That includes the DOM node that your responseMessageView instance is tied to. If you inspect the responseMessageView.el you'll actually see that it has been updated with the new model data, however the element isn't attached to the index's DOM tree anymore.

var Parent = Backbone.View.extend({
  template: _.template(''),
  render: function() {
    this.$el.html(this.template());
  },

  initialize: function() {
    this.render();
    this.child = new Child();
    this.$el.append(child.el);
  }
});

Here when you manually call render, child.el will no longer be in the parent.el (you can check using the inspector).

The simplest fix here is to call child.setElement after the parent renders with the newly rendered div#responseMessage element. The better fix is to detach the responseMessageView's element beforehand, and reattach after:

var Parent = Backbone.View.extend({
  render: function() {
    this.responseMessageView.$el.detach();
    this.$el.html(this.template(this.model.toJSON()));
    this.$el.find('#responseMessage').replaceWith(this.responseMessageView.el);
  }
});
like image 84
Justin Avatar answered Sep 28 '22 08:09

Justin