How can I do something work after transition to some route and render complete?


#1

Has someone any advice?


#2

You could use the “didTransition” action of your route. This will fire after the transition to the route completes.


#3

No, didTransition will be fired before didInsertElement, not after render complete.


#4
didInsertElement() {
  Ember.run.scheduleOnce('afterRender', this, () => {
    // ...
  });
}

#5

@jasonmit

Where I can place didInsertElement since viewClass was deprecated.

A template has many components. After these component rendered, I want to do something . I can call didInsertElement in a view of template before.

But now, how can I do this?


#6

Try placing Jason’s suggested run loop code in the didTransition action that I mentioned before. Or, didInsertElement is available in your components so you could put it there. However, that might not suit as you might want your after render code somewhere outside of a component so you have access to the model without having to pass it in via attributes to the component.


#7

Thank you.

Merged solution:

ApplicationRoute::didTransition => scheduleOnce(‘afterRender’)


#8

You should note that scheduling in afterRender at this point in the process DOES NOT guarantee that it happens after rendering is complete. If the runloop empties before rendering is finished, this will fire sooner. 99.9% of the time you will never know the difference.

You can still use top level views in ember, or utilize a component to wrap your route’s template, and use the didInsertElement hook there.


#9

@jthoburn

Using component to wrap route’s template is specific, not for purpose, and ApplicationView::didInsertElement too.

I don’t understand the runloop empties before rendering is finished.


#10

Using component to wrap route’s template is specific, not for purpose, and ApplicationView::didInsertElement too.

Yes, but both of these are common and acceptable practices. This is the way you tell your app to do something after render.

I don’t understand the runloop empties before rendering is finished.

The runloop is a set of queues. The queue can finish processing before rendering is complete. It’s not a guarantee that the afterRender queue runs after render is complete, just that it runs after the render queue is complete. The render queue is where tasks pertaining to rendering are scheduled, but what it considers to be render and what your browser considers to be render aren’t perfectly aligned.

The queues are for organizing the order of operations, they don’t guarantee timing. By using didInsertElement along with afterRender you guarantee yourself both timing and correct organization.


#11

@jthoburn

Thank you for your explanation.

There is a example.

Say that I need to scroll component content with using iscroll.js.

If the data of component content is explicit. use ApplicationRoute::didTransition => scheduleOnce(‘afterRender’, triggerSomethingForIscroll) It seem not working properly sometimes like you say.

use Component::didInsertElement => scheduleOnce(‘afterRender’, doIscrollFunction) It works fine.

But if the data of component content contains some asynchronous data which is from model relationships.

Is it right that the above way will not wait until asynchronous data loaded, so it will result to that iscroll get wrong height of content at the same time.

This is what I encountered.

Finally, does it exist a way to know render complete for any route transition globally?


#12

@imaxhung One strategy I used to combat a similar problem was to wait to initialize iscroll in a didInsertElement afterRender callback until the content was actually in the DOM. Count the number of elements in the DOM where you expect them. If the count is greater than zero, initialize the plugin.


#13

I generally just create a view for the route (like app/views/whatever.js) and add an afterRender function to the controller

 export default Ember.View.extend({
    didInsertElement: function() {
        if(this.get('controller').afterRender) {
            this.get('controller').afterRender();
        }   
    }   
});

~