2

I've noticed that a route doesn't re-render when the model changes (i.e. transitionTo the same route with a different model). I have some jQuery plugins set up on a particular page and I need them to re-render when the model changes, so it appears as a fresh page.

Is there a way to do this? Perhaps by observing the model's ID and firing a re-render of the route somehow?

Thanks in advance

Adam
  • 673
  • 1
  • 8
  • 18
  • I'm not sure I understand the question. Do you mean that you want your route to re-render when the model changes? So you want to do something like model.save(), and then you want the page to reload in order to re-run your jQuery plugins? Is there a hook into your jQuery plugin? Could you do something like `model.save().then(() => /* jQueryPlugin.refresh() /* })` ? – Will Haley Nov 08 '17 at 00:33
  • I just need the template to rerender when the route is entered, regardless of the previous route. The current Ember functionality is that if you transitionTo the same route with a different model, only the data bindings are updated - the view isn't re-rendered. – Adam Nov 08 '17 at 04:52
  • Aaaah, sorry, my mistake. I thought you were saying that the data-bindings _weren't_ updating for some reason. I understand now. You want not only the data bindings to update, but the entire template to re-render. – Will Haley Nov 08 '17 at 05:49

1 Answers1

2

I have an ember twiddle that, I believe, does what you're looking for, but first I would like to argue there are no straightforward ways to do what you're asking because it is the opposite of what an SPA is designed to do.

Data binding (without refreshing the view) is typically a boon of an SPA, and the SPA works hard to avoid brute force reloading/refreshing/rerendering the view at all costs. It took me a while to find a solution to your question as it is stated because it seems to go against Ember design principles. Even hooks like route refresh() are meant to update the model and bound data, not reload the template.

Although other people have asked the same question before, it seems that most answers guide users towards not refreshing the whole view. More often than not, the ideal of refreshing the view is an incorrect assumption.

Following previous examples, I would suggest that your goal shouldn't be to refresh the template completely, but rather, figure out how you can make your jQuery plugin better fit in to a Single Page App/client-side JS friendly design and have it reload as a natural part of the route lifecycle.

For instance, maybe the plugin can be reloaded/reset/re-run in afterModel() or somewhere similar.

That said, I was able to accomplish what you asked for using (in my opinion, a hack) Ember.run.later() so that I could invalidate an if block and force the content to rerender. Note, this is typically not what users want since (aside from design principle reasons) it causes UI flicker.

I have a component like so.

/* will-rerender.hbs */
{{#if show}}
    {{yield}}
{{/if}}

And it has a JS file like so.

/* will-rerender.js */
import Ember from 'ember';

export default Ember.Component.extend({
  show: false,

  didReceiveAttrs() {
    this._super(...arguments);

    /* 
      Ugly hack, but we need to reset `show` in
      a separate run loop in order to force the view
      to rerender.
    */

    this.set('show', false);

    Ember.run.later(() => {
      this.set('show', true);
    });
  }
});

You can invoke it like this...

/* your template */
{{#will-rerender cacheKey=model.id}}
   ... your content to rerender ...
{{/will-rerender}}

Whenever the model.id changes the component will invoke didReceiveAttrs() causing show to invalidate and the view to refresh.

As an aside, I think the behavior of switching between models would be much more natural with {{link-to}} rather than calling transitionTo yourself.

Will Haley
  • 703
  • 1
  • 9
  • 25
  • Thanks for this example, Will. I think my situation can be better described by saying that the reason the components need to re-render is that they in fact load data. Some of them are charts (Chart.js) that fetch the data themselves, rather an given data from the route. I think your solution will still work but I'm wondering if that additional insight could lead to a more elegant solution – Adam Nov 21 '17 at 03:25
  • Thanks for the additional context, Adam. If the end goal is to reload data for the charts, what about using the [Chart.js APIs for updating data](http://www.chartjs.org/docs/latest/developers/updates.html)? You could potentially bind an action in your component or controller to update the Chart.js data array. – Will Haley Nov 21 '17 at 04:21
  • Will, I did have success using the update data API, in combination with `ember-diff-attrs` and computed functions observing the model ID. Thanks for your help! – Adam Nov 21 '17 at 20:39
  • Awesome! Glad to hear you got it going Adam. – Will Haley Nov 22 '17 at 00:19