11

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!

suprita shankar
  • 1,554
  • 2
  • 16
  • 47
  • Are you intentionally passing new model to `responseMessageView ` or are you expecting the model you're referring to in this code to be same..? You seems to be doing `this.model.set('data', response.data);` `this.responseMessageView.model.set('data', response.data);` etc and looks like a mess... We don't know what is the *thing* containing the `initialize` in first code block... We don't know what is the *thing* containing the second code block.. you might want to explain the relationships between these views, models etc first – T J Oct 23 '15 at 20:18
  • @t-j I have edited the question. I do not understand what do you mean by thing? initialize() is used to declare a view for the whole page and for the partial. Sorry if I am not able to explain myself well. – suprita shankar Oct 23 '15 at 20:30
  • By *thing* I was asking whether it's a view, collection etc... I didn't get what you mean by "*Its just that when primaryViewSuccess is called and then responseViewSuccess its not taking the latest model changes."* which view is not taking which model changes..? You have 2 views and 2 models. Please explain based on that, who should update based on whom. – T J Oct 24 '15 at 04:05
  • One thing I don't understand is the reason for having separate models for both views... they seems to be using the same data `this.model.set('data', response.data);`... If you use thee same model then it'll be easier to keep both view in sync. Also, you don't have to manually set `response.data` into model. You can return `response.data` from `parse` method, it'll be set automatically. – T J Oct 24 '15 at 04:05
  • show me how your POST callback is written please – François Richard Oct 24 '15 at 06:58
  • @FrançoisRichard My Post function looks like this ```exports.sendEmail = function sendEmail(req, res){ callsomefunction(req, function(err, response){ req.model.data.success = true; responseRender.handleResponse(null, req, res); }); };``` – suprita shankar Oct 24 '15 at 07:09
  • @TJ I use the same model too. It still gives me the same error. `responseViewSuccess: function(model,response){ this.model.set('data', response.data); this.responseMessageView.render(); // Does not work in some cases }` .. This still does not work :( – suprita shankar Oct 24 '15 at 07:12
  • @TJ Answering the first question - There are 2 events occurring. One should trigger the `primaryViewSuccess` and the other `responseViewSuccess`. Each of them have their own models and views which needs to be updated. Now if I trigger responseViewSuccess it works perfectly fine. But when I trigger `primaryViewSuccess` followed by the `responseViewSuccess` it does not work. – suprita shankar Oct 24 '15 at 07:15
  • but how do you use it and update your model that's what I needf – François Richard Oct 24 '15 at 07:43
  • also where is the ajaxcall ? I need to see how you wrote the ajax call not only the callback – François Richard Oct 24 '15 at 07:44
  • @FrançoisRichard `this.model.save()` does the POST request.(this is the ajaxcall) `this.responseMessageView.model.set('data', response.data);` this updates the model and `this.responseMessageView.render()` should render the view. – suprita shankar Oct 24 '15 at 22:01

3 Answers3

5

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);
  }
});
jridgewell
  • 1,018
  • 1
  • 8
  • 12
  • This makes absolutely sense. But since I am using DustJS this does not work for me :/ .. I do `this.$el.html(this.template(this.model.toJSON()));` The parent view does not show the latest model changes. the html does not show the model changes. – suprita shankar Oct 26 '15 at 19:27
3

Try to use success callback and let me know I think your problem may come from here:

this.model.save( {att1 : "value"}, {success :handler1, error: handler2});

Also are you sure you want to use listenToOnce ? instead of listenTo ??

François Richard
  • 6,817
  • 10
  • 43
  • 78
  • Nope this does not work. My function looks like this `this.responseMessageView.model.save({data: {email: emailAddress.val()}});` ..Also I have to mention that the success callback is being fired for sure. As I have console statements in that. – suprita shankar Oct 25 '15 at 19:31
  • I tried this `this.responseMessageView.model.save({data: {email: emailAddress.val()}}, {success :this.responseViewSuccess, error: this.responseViewError});` .. This does not work :( what am I missing? – suprita shankar Oct 25 '15 at 19:34
  • I did listenTo and still no avail :/ – suprita shankar Oct 26 '15 at 18:06
3

Try doing this.delegateEvents() after your render (http://backbonejs.org/#View-delegateEvents).

craigmichaelmartin
  • 6,091
  • 1
  • 21
  • 25