When to use the AngularJS `$onInit` Life-Cycle Hook
Asked Answered
Q

1

13

With the release of AngularJS V1.7, the option to pre-assign bindings to has deprecated and removed:

Due to 38f8c9, directive bindings are no longer available in the constructor.

To migrate your code:

  • If you specified $compileProvider.preAssignBindingsEnabled(true) you need to first migrate your code so that the flag can be flipped to false. The instructions on how to do that are available in the "Migrating from 1.5 to 1.6" guide. Afterwards, remove the $compileProvider.preAssignBindingsEnabled(true) statement.

AngularJS Developer Guide - Migrating to V1.7 - Compile

Due to bcd0d4, pre-assigning bindings on controller instances is disabled by default. We strongly recommend migrating your applications to not rely on it as soon as possible.

Initialization logic that relies on bindings being present should be put in the controller's $onInit() method, which is guaranteed to always be called after the bindings have been assigned.

AngularJS Developer Guide - Migrating from v1.5 to v1.6 - $compile

What are the use cases when code has to be moved to the $onInit Life-Cycle Hook? When can we just leave the code in the controller construction function?

Qnp answered 10/7, 2018 at 20:35 Comment(1)
If code does not use any bindings, you can leave it -- or you think there is something more deep here?Foolhardy
R
10

Code has to be moved in the $onInit function, when it depends on bindings, because these bindings are not available within this in the constructor. They get assigned AFTER instantiation of the component class.

Example: You have a state definition like this:

$stateProvider.state("app", {
  url: "/",
  views: {
    "indexView": {
      component: "category"
    }
  },
  resolve: {
    myResolve: (someService) => {
      return someService.getData();
    }
  }
});

You can bind the result of myResolve to your component like this:

export const CategoryComponent = {
  bindings: {
    myResolve: "<"
  },
  controller: Category
};

If you now log out this.myResolve in the constructor and in $onInit you will see something like this:

constructor() {
  console.log(this.myResolve); // <-- undefined
}

$onInit() {
  console.log(this.myResolve); // <-- result of your resolve
}

So, your constructor should only contain constructing code like:

constructor() {
  this.myArray = [];
  this.myString = "";
}

Every angular specific initialisation and binding or dependency usage should be in $onInit

Ridley answered 20/7, 2018 at 13:23 Comment(2)
Trying to think of why I'd leave something in the constructor proper and not just go ahead and place everything in $onInit(). I understand you could argue that's not as expressive as grouping non-binding-related code in the constructor, but that's the best reach I've got so far.Barbi
From an AngularJs point of view, you are totally right. $onInit is mostly sufficient. But keep in mind, that $onChanges runs before $onInit (in AngularJs 1.5.5 and above) and you might use uninitialized properties. From an OOP point of view, initializing in the constructor is preferred.Ridley

© 2022 - 2024 — McMap. All rights reserved.