# @lifecycleHooks does not work

**URL:** <https://discourse.aurelia.io/t/lifecyclehooks-does-not-work/4699>\
**Category:** Uncategorized\
**Created:** [March 17, 2022, 9:25am UTC](https://discourse.aurelia.io/t/lifecyclehooks-does-not-work/4699 "2022-03-17T09:25:35Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![hoangcuongvn](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/hoangcuongvn/32/2271_2.png) [@hoangcuongvn](https://discourse.aurelia.io/u/hoangcuongvn)\
**Post date:** [March 17, 2022, 9:25am UTC](https://discourse.aurelia.io/t/lifecyclehooks-does-not-work/4699/1 "2022-03-17T09:25:35Z")

</div>

I created a simple hook class

```auto
@lifecycleHooks()
export class UserLifecycleHooks {
	constructor(@IUserState readonly user: IUserState) {
	}

	created(vm: any): void {
		vm["$user"] = this.user;
	}
}

```

And I register it as a global hook

```auto
Aurelia
  .register(UserLifecycleHooks)
  .app(App)
  .start();

```

But it does not work. The hook’s constructor is called (that means the hook registry is fine). But the hook’s method “created” is not called when components (custom elements) are created.

Did I miss something?

---

<div class="post-metadata">

**Author:** ![Sayan751](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/sayan751/32/1861_2.png) [@Sayan751](https://discourse.aurelia.io/u/Sayan751)\
**Post date:** [March 17, 2022, 7:00pm UTC](https://discourse.aurelia.io/t/lifecyclehooks-does-not-work/4699/2 "2022-03-17T19:00:58Z")

</div>

Hi @hoangcuongvn! I think you might be confusing the routing lifecycle hooks with component lifecycle hooks. AFAIK the hooks you define with the `@lifecycleHooks()` are the routing hooks which can be one of the following: `canLoad`, `load`, `canUnload`, `unload`.

---

<div class="post-metadata">

**Author:** ![hoangcuongvn](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/hoangcuongvn/32/2271_2.png) [@hoangcuongvn](https://discourse.aurelia.io/u/hoangcuongvn)\
**Post date:** [March 18, 2022, 1:44am UTC](https://discourse.aurelia.io/t/lifecyclehooks-does-not-work/4699/3 "2022-03-18T01:44:31Z")

</div>

@Sayan751 According to the link below, @lifecycleHooks attribute is for component lifecycle hooks.

> <https://github.com/aurelia/aurelia/issues/1044>
>
> \<!---
> Thanks for filing an issue 😄 ! Before you submit, please read the followi…ng:
> 
> Search open/closed issues before submitting since someone might have asked the same thing before!
> \--\>
> 
> \# 💬 RFC
> 
> \### NOTE: This proposal is deliberately minimalistic and intended as an MVP to be included in alpha
> 
> This RFC proposes a unified API to share common logic between components, intended to be run at specific lifecycle hook timings.
> 
> The \`@lifecycleHooks\` decorator will be a direct replacement for v1's \`@viewEngineHooks\`, as well as v1 router's pipeline slots, with improved consistency and leaving room for (potentially unknown) future enhancements.
> 
> Unlike \`@viewEngineHooks\`, there is not a pre-determined set of hooks that can be tapped into. This allows users and plugin authors to define new hooks if they want to, in much the same way that Aurelia itself (for starters the runtime and the router) would be utilizing it.
> 
> \### Might address these issues and/or is related to
> \- https://github.com/aurelia/aurelia/issues/978
> \- https://github.com/aurelia/aurelia/issues/1037
> 
> \### TLDR (what does the API look like?)
> 
> \`\`\`js
> // do-stuff.js
> export class DoStuffLifecycleHooks {
> binding(vm, ...args) {}
> attaching(vm, ...args) {}
> detaching(vm, ...args) {}
> // etc
> }
> 
> // consumer.js
> export class ConsumerCustomElement {
> static dependencies = \[DoStuff\]; // Or register globally
> }
> \`\`\`
> 
> \## 🔦 Context
> 
> \### Use cases
> A common requirement in apps is to run the same bit of logic in the same lifecycle hook in multiple components. Some examples:
> 1) \`\*\`: Conditionally log each lifecycle hook invocation for advanced troubleshooting.
> 2) \`binding\`, \`load\`: Lazy load data from the server only once, but initiate this from whichever component (out of several that need it) happens to be rendered first.
> 3) \`define\`, \`hydrating\`, \`hydrated\`, \`created\`: Apply CSS to components matching arbitrary/dynamic criteria or other custom runtime conventions
> 4) \`attaching\`, \`detaching\`: Run the same enter/leave animations for all routed components.
> 5) \`canLoad\`: Run auth guards for all components or a (potentially dynamic) subset thereof.
> 6) \`canUnload\`: Run an "unsaved changes" dialog guard for all components that have an \`isDirty\` getter on them.
> 
> \### Prior art
> This API carries some similarities from concepts from other frameworks:
> \- React: \[higher-order components\](https://reactjs.org/docs/higher-order-components.html)
> \- Vue: \[mixins\](https://vuejs.org/v2/guide/mixins.html)
> \- In Angular and Aurelia 1, you would do this sort of thing either via TypeScript mixins, or manual aggregation ("composition over inheritance").
> 
> \### Resource semantics
> 
> When \`@lifecycleHooks\` is applied to a class, that class becomes a "resource" (it's registered to the \`Protocol.resource\` metadata api just like \`@customElement\`, \`@valueConverter\`, etc), giving it resource-specific semantics for DI and the runtime module loader/analyzer:
> \- Registering it in the root DI container (e.g. via \`Aurelia.register\`) turns it into a global resource and causes it to be invoked for \*every\* component
> \- Adding it to the \`dependencies\` list of a specific component, causes it to be invoked for only that component.
> \- It's recognized by the module analyzer, meaning it can lazy loaded (natively or with help of webpack / other bundlers) with relative ease.
> \- It will be supported by conventions (by adding the \`LifecycleHooks\` suffix to the class name).
> \`\`\`ts
> // explicit
> @lifecycleHooks()
> export class SharedStuff { ... }
> 
> // conventions:
> export class SharedStuffLifecycleHooks { ... }
> \`\`\`
> 
> The above are pretty much freebies for each API that adheres to the resource protocol, and \`@lifecycleHooks\` seems like a logical consumer of this existing infrastructure.
> 
> \### Contracts & behavior
> 
> \- The first argument passed-in is always the ViewModel, followed by the arguments that are normally passed-in to these hooks.
> \- \`LifecycleHook\`s are \*scoped singletons\*, just like \`ValueConverter\`s and \`BindingBehavior\`s, meaning globally registered ones are global singletons, whereas locally registered ones are singletons only within their registered context (typically one instance per type)
> \- Shared hooks run \*before\* (and if async, in parallel with) their component instance counterparts.
> 
> \### Future extensions
> 
> Depending on the feedback during alpha, we may add some quality-of-life configuration options to the \`@lifecycleHooks\` to enable more use cases and/or improve performance. For example:
> \`\`\`ts
> // Control whether the hook runs before, after, or in parallel with the component instance hook.
> // This would default to 'parallel' (which is the current only behavior), making this a non-breaking change.
> @lifecycleHooks({ order: 'before' | 'after' | 'parallel' })
> \`\`\`
> 
> \`\`\`ts
> // Apply a compile-time filter that, when it returns true, determines whether or not this hook will run for the specified component.
> // Strictly speaking this does not add a new feature, but it could counteract the potentially significant performance impact
> // of many shared hooks across an app that are only meant to run on a few components
> @lifecycleHooks({ match: (definition: IResourceDefinition) =\> boolean })
> \`\`\`
> 
> 
> 
> 
> 
> 
> \## 💻 Examples
> 
> 
> 
> Let's go through the aforementioned use cases, now with example implementations using this new API:
> \### 1) \`\*\`
> 
> Conditionally log each lifecycle hook invocation for advanced troubleshooting.
> 
> \- Registration scope: global
> 
> \`\`\`js
> // lifecycle-logger.js
> @lifecycleHooks()
> export class LifecycleLogger {
> constructor(@ILogger logger) { this.logger = logger; }
> define(vm) { this.trace('define', vm); }
> hydrating(vm) { this.trace('hydrating', vm); }
> hydrated(vm) { this.trace('hydrated', vm); }
> created(vm) { this.trace('created', vm); }
> binding(vm) { this.trace('binding', vm); }
> bound(vm) { this.trace('bound', vm); }
> attaching(vm) { this.trace('attaching', vm); }
> attached(vm) { this.trace('attached', vm); }
> detaching(vm) { this.trace('detaching', vm); }
> unbinding(vm) { this.trace('unbinding', vm); }
> canLoad(vm) { this.trace('canLoad', vm); return true; }
> load(vm) { this.trace('load', vm); }
> canUnload(vm) { this.trace('canUnload', vm); return true; }
> unload(vm) { this.trace('unload', vm); }
> trace(hook, vm) { this.logger.trace(\`${hook} ${vm.$controller.definition.name}\`); }
> }
> 
> // index.js
> Aurelia
> .register(
> LifecycleLogger,
> ...,
> )
> .app(...)
> .start();
> \`\`\`
> 
> \### 2) \`binding\`, \`load\`
> 
> Lazy load data from the server only once, but initiate this from whichever component (out of several that need it) happens to be rendered first.
> 
> \- Registration scope: local (added by hand to \`dependencies\`)
> 
> (in this example, we use it for a dropdown that needs some server-side data that only needs to be loaded once)
> 
> \`\`\`js
> // things-initializer.js
> export const ICommonThings = DI.createInterface().withDefault(x =\> x.singleton(CommonThings));
> export class CommonThings {
> constructor(@IHttpClient http) { this.http = http; }
> init() {
> if (!this.isInitialized) {
> return this.initPromise ??= (async () =\> {
> this.data = await this.http.get('api/common-things');
> this.isInitialized = true;
> })();
> }
> }
> }
> @lifecycleHooks
> export class ThingsInitializer {
> constructor(@ICommonThings things) { this.things = things; }
> binding() { return this.things.init(); }
> load() { return this.things.init(); }
> }
> // some-dropdown.js
> @customElement({
> name: 'some-dropdown',
> template: '\<select :value="thingId"\>\<option repeat.for="thing of things.data" :model="thing.id"\>${thing.name}\</option\>\</select\>',
> dependencies: \[ThingsInitializer\],
> })
> export class SomeDropdown {
> constructor(@ICommonThings things) { this.things = things; }
> }
> \`\`\`
> 
> \### 3) \`define\`, \`hydrating\`, \`hydrated\`, \`created\`
> 
> Apply CSS to components matching arbitrary/dynamic criteria or other custom runtime conventions
> 
> \- Registration scope: global
> 
> \`\`\`js
> // ripple-effect-applicator.js
> @lifecycleHooks
> export class RippleEffectApplicator {
> created(vm) {
> if (vm.$controller.definition.name.startsWith('bs-') && vm.someCondition) {
> // (note: this definition.instructions stuff will become easier to use soon)
> vm.$controller.definition.instructions\[0\].push(new HydrateAttributeInstruction('ripple', \[\]));
> }
> }
> }
> 
> // index.js
> Aurelia
> .register(
> RippleEffectApplicator,
> ...,
> )
> .app(...)
> .start();
> \`\`\`
> 
> \### 4) \`attaching\`, \`detaching\`:
> 
> Run the same enter/leave animations for all routed components.
> 
> \- Registration scope: local (added by custom decorator)
> 
> \`\`\`js
> // animations.js
> const duration = 300;
> export class SlideInOutAnimations {
> attaching(vm) {
> return vm.$controller.host.animate(\[{ transform: 'translate(-100vw, 0)' }, { transform: 'translate(0, 0)' }\], { duration, easing: 'ease-in' }).finished;
> }
> detaching(vm) {
> return vm.$controller.host.animate(\[{ transform: 'translate(0, 0)' }, { transform: 'translate(100vw, 0)' }\], { duration, easing: 'ease-out' }).finished;
> }
> }
> export class FadeInOutAnimations {
> attaching(vm) {
> return vm.$controller.host.animate(\[{ opacity: 0 }, { opacity: 1 }\], { duration }).finished;
> }
> detaching(vm) {
> return vm.$controller.host.animate(\[{ opacity: 1 }, { opacity: 0 }\], { duration }).finished;
> }
> }
> // let's make a convenient decorator for ourselves this time:
> export function pageTransitions(target) {
> (target.dependencies ??= \[\]).push(SlideInOutAnimations, FadeInOutAnimations);
> }
> 
> // home-page.js
> @pageTransitions
> export class HomePage {}
> 
> // settings-page.js
> @pageTransitions
> export class SettingsPage {}
> \`\`\`
> 
> \### 5) \`canLoad\`:
> 
> Run auth guards for all components or a (potentially dynamic) subset thereof.
> 
> \- Registration scope: global
> 
> \`\`\`js
> // auth-guard.js
> @lifecycleHooks
> export class AuthGuard {
> constructor(@IAuthService authService) { this.authService = authService; }
> // (note: router hook signatures are still subject to change)
> async canLoad(vm, params, next, current) {
> if (!current.data.auth || await this.authService.isAuthorized(current.data.auth.scopes)) {
> return true;
> }
> return \`/login(reason=${current.data.auth.scopes})\`;
> }
> }
> 
> // settings-page.js
> @route({ data: { auth: { scopes: \['settings'\] } } })
> export class SettingsPage {}
> 
> // profile-page.js
> @route({ data: { auth: { scopes: \['profile'\] } } })
> export class ProfilePage {}
> 
> // index.js
> Aurelia
> .register(
> AuthGuard,
> ...,
> )
> .app(...)
> .start();
> \`\`\`
> 
> \### 6) \`canUnload\`
> 
> Run an "unsaved changes" dialog guard for all components that have an \`isDirty\` getter on them.
> 
> \- Registration scope: global
> 
> \`\`\`js
> // dirty-state-guard.js
> @lifecycleHooks
> export class DirtyStateGuard {
> constructor(@IDialogService dialog) { this.dialog = dialog; }
> async canUnload(vm, params, next, current) {
> if (vm.isDirty) {
> return this.dialog.promptUnsavedChanges();
> }
> return true;
> }
> }
> 
> // index.js
> Aurelia
> .register(
> DirtyStateGuard,
> ...,
> )
> .app(...)
> .start();
> \`\`\`
> 
> \<!-- Love Aurelia? Please consider supporting our collective:
> 👉 https://opencollective.com/aurelia --\>

Here is a sample from the link.

```auto
// lifecycle-logger.js
@lifecycleHooks()
export class LifecycleLogger {
  constructor(@ILogger logger) { this.logger = logger; }
  define(vm) { this.trace('define', vm); }
  hydrating(vm) { this.trace('hydrating', vm); }
  hydrated(vm) { this.trace('hydrated', vm); }
  created(vm) { this.trace('created', vm); }
  binding(vm) { this.trace('binding', vm); }
  bound(vm) { this.trace('bound', vm); }
  attaching(vm) { this.trace('attaching', vm); }
  attached(vm) { this.trace('attached', vm); }
  detaching(vm) { this.trace('detaching', vm); }
  unbinding(vm) { this.trace('unbinding', vm); }
  canLoad(vm) { this.trace('canLoad', vm); return true; }
  load(vm) { this.trace('load', vm); }
  canUnload(vm) { this.trace('canUnload', vm); return true; }
  unload(vm) { this.trace('unload', vm); }
  trace(hook, vm) { this.logger.trace(`${hook} ${vm.$controller.definition.name}`); }
}

// index.js
Aurelia
  .register(
    LifecycleLogger,
    ...,
  )
  .app(...)
  .start();

```

---

<div class="post-metadata">

**Author:** ![Sayan751](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/sayan751/32/1861_2.png) [@Sayan751](https://discourse.aurelia.io/u/Sayan751)\
**Post date:** [March 18, 2022, 5:23am UTC](https://discourse.aurelia.io/t/lifecyclehooks-does-not-work/4699/4 "2022-03-18T05:23:55Z")

</div>

The issue is still open that means that it is not yet fully implemented. However, you can leverage the component hooks in individual custom elements.

---

<div class="post-metadata">

**Author:** ![hoangcuongvn](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/hoangcuongvn/32/2271_2.png) [@hoangcuongvn](https://discourse.aurelia.io/u/hoangcuongvn)\
**Post date:** [March 18, 2022, 7:14am UTC](https://discourse.aurelia.io/t/lifecyclehooks-does-not-work/4699/5 "2022-03-18T07:14:39Z")

</div>

Ah, I thought it was implemented because that thread was long time ago.

I am curious how Au2 framework injects the property `$controller` into every VM?

---

<div class="post-metadata">

**Author:** ![vladyn](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/vladyn/32/2473_2.png) [@vladyn](https://discourse.aurelia.io/u/vladyn)\
**Post date:** [February 24, 2024, 10:02am UTC](https://discourse.aurelia.io/t/lifecyclehooks-does-not-work/4699/6 "2024-02-24T10:02:31Z")

</div>

Are these lifehooks for Aurelia 2 or 1? Or both?
