blueFast
blueFast

Reputation: 44351

Index view not refreshing after receiving updated data from backend

I am testing my application, so I am doing the following:

  1. I show an index view (#/locators/index), of Locator objects, which I initially load with App.Locator.find();
  2. I modify the backend manually
  3. Manually (with a button/action) I trigger a refresh of the data in the ember frontend, without changing the route. I do this with App.Locator.find().then(function(recordArray) {recordArray.update();});. I see via console logging that a list request is sent to the backend, and that the up-to-date data is received. I assume this is used to update the store.
  4. BUT: The view does not update itself to show this new data

Why does the view not get automatically updated when the store receives new data? Isn't that the whole point of the data binding in Ember?

If I now do the following:

  1. Open any other route
  2. Go back to the locators index route (#/locators/index)
  3. Ember sends a new request to list the locators
  4. The index view is shown, with the correct data (since it was already in the store?)
  5. New data is received

(I am not 100% sure that 4 and 5 happen in that order, but I am quite certain)

So, my impression is that the data is properly updated in the store, but that somehow a full re-rendering of the view is needed to display this new data, for example by leaving and re-entering the route. Is this true? Can I force this re-rendering programmatically?

Upvotes: 2

Views: 1901

Answers (3)

Vishnu Vardhana
Vishnu Vardhana

Reputation: 497

Ember changes view data when the underlying model is changed by the controller(Which is binded to the view)

(Only when the state of the application changes(url changes) router hooks are called) Your problem could be solved when you do this.refesh() inside your route by capturing the action triggered by your view.

App.IndexRoute = Ember.Route.extend({
    actions: {
        dataChanged: function() {
            this.refresh();
        }
       },
       //rest of your code goes here

        });

for this to work your handlebar template which modifies the data shoud have an action called dataChanged

example :

Assume this action is responsible for changing/modifying/deleting the underlying data

<button {{action 'dataChanged'}}> Change Data </button>

Refresh method actually does a model refresh and passes it to the corresponding controller which indeed changes the view.

Upvotes: 3

intuitivepixel
intuitivepixel

Reputation: 23322

There a couple of things that come to mind you could try:

If you are inside of an ArrayController force the content to be replaced with the new data:

this.replaceContent(0, recordArray.get('length'), recordArray);

Or try to call reload on every single record trough looping the recordArray:

App.Locator.find().then(function(recordArray) {
  recordArray.forEach(function(index, record) {
    record.reload();
  }
}

And if the second approach works, you could also override the didLoad hook in your model class without having to loop over them one by one:

App.Locator = DS.Model.extend({
  ...
  didLoad: function(){
    this.reload();
  }
});

If this works and you need this behaviour in more model classes consider creating a general mixin to use in more model classes:

App.AutoReloadMixin = Ember.Mixin.create({
  didLoad: function() {
    this._super();
    this.reload();
  }
});

App.Locator = DS.Model.extend(App.AutoReloadMixin, {
  ...
});

App.Phone = DS.Model.extend(App.AutoReloadMixin, {
  ...
});

Update in response to your answer

Handlebars.registerHelper is not binding aware, I'm sure this was causing your binding not to fire. You should have used Handlebars.registerBoundHelper or simply Handlebars.helper which is equivalent:

Handlebars.helper('grayOutIfUndef', function(property, txt_if_not_def) {
  ...
});

Hope this helps.

Upvotes: 1

blueFast
blueFast

Reputation: 44351

Somehow this seems to be due to the fact that I am using custom handlebar helpers, like the following:

Handlebars.registerHelper('grayOutIfUndef', function(property, txt_if_not_def) {
    // HANDLEBARS passes a context object in txt_if_not_def if we do not give a default value
    if (typeof txt_if_not_def !== 'string') { txt_if_not_def = DEFAULT_UNDEFINED_STR; }
    // If property is not defined, we return the grayed out txt_if_not_def
    var value = Ember.Handlebars.get(this, property);
    if (!value) { value = App.grayOut(txt_if_not_def); }
    return new Handlebars.SafeString(value);
});

Which I have been using like this:

{{grayOutIfUndef      formattedStartnode}

Now I have moved to a view:

{{view App.NodeIconView nodeIdBinding="outputs.startnode"}}

Which is implemented like this:

App.NodeIconView = Ember.View.extend({
    render: function(buffer) {
        var nodeId = this.get('nodeId'), node, html;
        if (nodeId) {
            node = App.getNode(nodeId);
        }
        if (node) {
            html = App.formattedLabel.call(node, true);
        } else {
            html = App.grayOut(UNDEFINED_NODE_NAME);
        }
        return buffer.push(html);
    }
});

I am not sure why, but it seems the use of the custom handlebars helper breaks the property binding mechanism (maybe my implementation was wrong)

Upvotes: 0

Related Questions