Constant Meiring
Constant Meiring

Reputation: 3325

Understanding and using Ember Component lifecycle hooks

I'm trying to figure out why an Ember component is not working (and trying to learn about component lifecycles in the process). The component in question is Ember-cli-mapbox. It uses nested components. You can have a mapbox-map component, and within that component you can have several mapbox-marker components. Now, how it is supposed to work, is the mapbox-map component initialises the map, and then passes a block to the child marker components. The child marker components then references the map that got passed down to them. An example of the components in use (which come from the component docs):

{{#mapbox-map mapId='ember-cli-mapbox.7c3914f2' as |map|}}
  {{#each positions as |position|}}
    {{mapbox-marker map=map coordinates=position.coordinates}}
  {{/each}}
{{/mapbox-map}}

Now, the components get set up using the didInsertElement hook, which makes sense to me since the DOM needs to be in place before the mapbox-map component can bind to a element in the dom. It doesn't work like that though. The didInsertElement of the child components get executed before the didInsertElement hook in the parent component. So, the marker tries to reference the map before it was created. I figured this out by putting console.logs in the component init code. I can't find much documentation on component lifecycles. didInsertElement does get referenced in the API docs here, but it seems that the newest API docs are actually out of date and don't reference a bunch of other hooks described here. The latter link says that life cycle events happen in the following order:

didInitAttrs
didReceiveAttrs
willRender
didInsertElement
didRender

Now, things get weird. When I replace didInsertElement in the components with didInitAttrs, it fires in the correct order. The didInitAttrs hook on the parent component fires first, followed by the child component didInitAttrs hooks. Problem with this is, the DOM isn't ready yet, so it doesn't help much. I also can't put the map binding event in the Ember runloop, since it need to be returned and passed as a block to the child elements.

So, my questions are:

  1. Why, when using didInsertElement on components, do the hooks get executed in the order they do? (children, then parents)
  2. How did this component ever work in the way it's currently written?
  3. Am I supposed to use the above mentioned hooks if they're not mentioned in the official API docs?

I've recreated the addon in an Ember Twiddle here. Child hooks get called before the parent hooks, causing the component to break since map is undefined when the hook is called. This happens on Ember 1.13.8 as well as 1.13.9.

Upvotes: 3

Views: 2574

Answers (1)

Gaurav
Gaurav

Reputation: 12796

Why, when using didInsertElement on components, do the hooks get executed in the order they do? (children, then parents)

This was changed in version 1.8. It was previously parent, then children but this often required people to use some complicated method of waiting for children to render to do certain things. Changing the order made learning Ember simpler.

See https://github.com/emberjs/ember.js/issues/5631 for more information.

How did this component ever work in the way it's currently written?

I have not used this addon, and have no idea if it works or not. I fixed your twiddle to work, however: http://ember-twiddle.com/4c3e55d0a66ead378bdf

Am I supposed to use the above mentioned hooks if they're not mentioned in the official API docs?

These hooks are not mentioned because the documentation is still catching up to changes in Ember. Feel free to use them if you'd like.

Upvotes: 4

Related Questions