Briebug Blog

Sharing our thoughts with the community

What is the difference between Constructor and ngOnInit?

What is the difference between Constructor and ngOnInit?


The constructor and ngOnInit are both available lifecycle hooks when developing Angular applications. Both provide specific benefits, and understanding the difference allows you to develop predictable, extendable, and testable applications. Often the constructor ends up doing the majority of work during class initialization, However, ngOnInit may be better suited depending on your task.


Angular applications rely heavily on classes. When writing a component, you'll typically find a constructor declared inside a class:



MDN defines the constructor as:

a special method for creating and initializing an object created with a class. There can only be one special method with the name "constructor" in a class.


The constructor has a particular purpose inside an Angular application with unique features provided by the ECMAScript 2015 specification, Typescript, and Angular itself. Let's look at each in more detail.




ECMAScript 2015 constructor

Javascript classes were introduced in ECMAScript 2015 and allow you to initialize and customize a class's instantiation by utilizing the constructor. In essence, you can construct new classes and control that construction with the constructor method. By defining parameters in the constructor, you can control how you instantiate or "new up" a class by passing different arguments similar to normal functions.


Let's assume we have a Logger that logs messages to the UI and the server:



The constructor has two main jobs in this example:

  • Setting a member property (this.fmt) inside the constructor. This block executes as soon as the class instantiates.
  • Accept arguments to control how messages are formatted when creating a new Logger class.




Typescript constructor

Typescript adds some syntactic sugar on top of the ECMAScript specification. Everything above is still valid when using Typescript and constructors, However, you can do more with less code. When defining parameters in the constructor, we can leverage parameter properties to create and initialize a member in one place.



By declaring the constructor parameter with either public, private, or protected it creates and initializes the member in place, allowing you to access this.fmt inside the class, just like the previous example.




Angular constructor

Angular applications build on the concept of dependency injection. When a class is initialized and requests an injectable resource, dependency injection finds the resource and provides it to the requestor. During the lifetime of an application this process may happen once or many times, depending on how the class is consumed.


Angular leverages all the benefits listed so far and takes it one step further. By declaring a constructor parameter with an injectable type, Angular can search the dependency tree and find the closest resource. This resolution is possible because of dependency injection, modules, and decorators. Using our previous example with Angular looks like this:



Here we combine everything mentioned so far:

  • Accept an argument to control how the Logger class functions regarding the formatting.
  • Create and initialize the member property (this.fmt) in the constructor
  • We leverage dependency injection to control which "FormatService" class the Logger class receives.




ngOnInit

The features available in an Angular class constructor are powerful yet concise, However, there's still more control and benefits available in the ngOnInit lifecycle method.

Angular lifecycle hooks

Components and directives start their lifecycle when Angular instantiates the class and renders any relevant views. It continues in sequence through change detection and ends when the component or directive is destroyed.


ngOnInit vs constructor

The ngOnInit lifecycle hook should contain complex initialization, such as fetching data or accessing other classes. Testing is made more accessible by moving initialization logic into a method you can call when needed instead of every class initialization. If you require access to inputs, it's important to know that data-bound properties might not be available until the ngOnInit hook. For example, directives do not have their data-bound properties set until after construction, making ngOnInit the appropriate location to initialize and interact with data-bound properties. These reasons make ngOnInit useful for:


  • Initialization (complex or not)
  • Fetching data
  • The first interaction with data-bound properties


The constructor should be used to manage dependencies and set initial member variables to simple values. Managing dependencies through dependency injection in the constructor provides more benefits rather than a manual approach. If a constructor were to manually "new up" a class, it would be violating the Single Responsibility Principle and close off any reuse opportunities that might otherwise be available for that class. These reasons make the constructor useful for:


  • Requesting dependencies through dependency injection
  • Creating and initializing members in one place with parameter properties
  • Set initial member variables to simple values


Below is a typical example of using both the constructor and ngOnInit while maintaining a predictable and testable component.





Summary

Hopefully, it's clear where each has its strengths. The constructor should favor simple tasks, and dependency injection while ngOnInit should handle complex tasks, initialization, and data-bound properties.


When deciding between these two, it's helpful to ask how easy is this to test? If complex initialization logic exists in the constructor, then you force every test and collaborator of that class to own the initialization burden and reduce reuse opportunities.


If you can remove the difficulty of constructing the class in isolation, testing is simplified and predictable. By utilizing ngOnInit, you'll be able to control when initialization occurs during testing and only introduce test setup cost when necessary.



References:

View Details
- +
Sold Out