Order of components lifecycle between components, plus router

I can find nothing that tells me the order of the components lifecycle with respect to other components.
See https://aurelia.io/docs/fundamentals/creating-components#the-component-lifecycle

Nor how the routing lifecycle is interweaved with the component lifecycle.
See https://aurelia.io/docs/fundamentals/cheat-sheet#routing

The best I can find is related to Dialogs.
See https://aurelia.io/docs/plugins/dialog#lifecycle-hooks

Order of Invocation

Each dialog instance goes through the full lifecycle once.

constructor call
.canActivate() - aurelia-dialog specific
.activate() - aurelia-dialog specific
.created() - as defined by aurelia-templating
.bind() - as defined by aurelia-templating
.attached() - as defined by aurelia-templating
.canDeactivate() - aurelia-dialog specific
.deactivate() - aurelia-dialog specific
.detached() - as defined by aurelia-templating
.unbind() - as defined by aurelia-templating

If I’m trying to share application state I need to know which component should be creating that state.

My current assumption was that custom elements higher up in the DOM would have their lifecycle called first so that its children would have whatever was needed by the time it was their turn for the lifecycle events.

Whacking in some debugs is showing me:

  • activate on Parent
  • activate on Child
  • created on Child
  • bind on Child
  • created on Parent
  • bind on Parent
  • attached on Parent
  • attached on Child

I’ll have to ponder this some more.

What advice do you have for me?
If I have remote resources that need obtaining where is the correct place in the lifecycle to fetch them?

1 Like

Note the wrong order of bind (parent should be before child) is a know bug (introduced by a fix to another bug).

That’s not the only lifecycle ordering bug, there is another one in child route https://github.com/aurelia/router/pull/571.

It was left as it is, because of current router has no enough test coverage. We are afraid of introducing another bug from another fix.

On the other side, Aurelia 2 has made it deadly clear about the lifecycle orders in test cases.

1 Like

In router components, I do remote fetch in activate.
In other custom elements, I do remote fetch in attached.

You can return a promise in all callbacks.

When remote fetch is slow, I recommend to have a global spinner, so that you don’t have to do spinner in every single route pages.

In top template (like app.html). debounce means you don’t show spinner if it’s quicker than 200ms (default debounce delay).

<my-spinner 
  if.bind="router.isNavigating || fetchClient.isRequesting & debounce"
></my-spinner>
2 Likes

Where is the return a promise in all callbacks documented?

I know activate supports that because https://aurelia.io/docs/fundamentals/cheat-sheet#routing says so.

I’m sure I tried returning a Promise for other callbacks and it wasn’t working as I thought it might, and since I couldn’t find the docs to say otherwise I assumed Promises were not awaited on.

Thanks for your thoughts, I’ll ponder what I should do properly some more.

1 Like

I could not find proper document either :sweat_smile: @bigopon do you know is this documented?

They do accept promise return, Aurelia will wait for them. This is a nice feature since Durandaljs (although I did not use it) before Aurelia. Async/await is same, it is syntax sugar for promise. (I was all wrong :smile:)

Show me your code which wasn’t working as you thought.

1 Like

That code is long gone :slight_smile:

I was hacking to see why things were not working the way I expected, which I assume is my lack of understanding rather than bugs.

1 Like

@huochunpeng

I’ve tried returning a Promise from bind expecting that to halt any further components bind being called, or at the least to wait until resolving before moving on to the further steps in the lifecycle like attached

But its not.

I don’t have a small reproducible test case yet. If I can sort out my production code I’ll work on that after.

1 Like

You are right. I was wrong. It looks like bind did not wait on promise. I will check if this is intentional. For now, use attached to wait on remote fetch.

1 Like

The templating lifecycles have never awaited promises in v1, only the router lifecycles do. In v1 you normally fetch data in activate rather than in bind. It’s possible though with composition transactions.
In v2 you can return a promise from bind and it will be awaited :slight_smile:

1 Like

I was all wrong :sweat_smile: no wonder no documentation.

Only router lifecycle canActivate, activate, canDeactive, deactivate supports promise return.

I return promise in attached, but it actually renders before the promise is resolved.

1 Like

I wish I could find the damn web article. It’s something along the lines of ensuring you test your component when it doesn’t have data.

I know its the way our components are (poorly) written. Right now the component fetches from the server to retrieve the settings for the component, as we persist those settings and its changes.

I’ve got a place holder for Improving vCurrent docs and it may very well be the way I look at the lifecycle hooks, but because activate doesn’t have anything injected into it and it hasn’t been created I don’t think activate is the correct place to fetch those values and then store them into the object as its not at the right stage in the lifecycle for touching member properties.

Yet if I don’t do it there the child components that depend on those settings won’t have anything available yet.

The code currently does the fetch and setting of this member properties in the activate and so I’m attempting to clean up the code and document what each lifecycle’s purpose is for and what types of things you should be doing in that method. We don’t have that level of understanding internally yet.

Note: My claim that it didn’t have anything injected into it was because it was a fresh restart of the server and the injected object hadn’t been setup yet. The constructor is the first thing in the lifecycle, which includes all injected properties. Whether your code has setup those objects is your problem, not Aurelia’s.

1 Like

The route activate callback is before created and bind, that is a debatable decision. But it doesn’t matter because you don’t have any binding on a route component.

The activate callback is designed for you to fetch remote data before rendering route component. This is demonstrated in tutorial. https://aurelia.io/docs/tutorials/creating-a-contact-manager#building-out-the-contact-detail-screen

2 Likes

Its very likely that our component is doing too much and should be broken into 2 components.

One to do the routing, one to do UI.

That expert advice is definitely something I want to add to the documentation.

2 Likes

Could you explain this advice a bit more, how would a component look like that only does routing and no rendering?