Get super confident at Angular. Angular Icon

I promise to change your Angular skills!

Angular Constructor versus OnInit

In this article you’ll learn the different between OnInit (the ngOnInit lifecycle hook) versus the constructor inside of a TypeScript class, and how to choose the right approach.

If you’re fairly new to Angular or have been working with it a while, you may be asking questions like:

“Should I inject dependencies in the constructor and also bind data?” “When should you use ngOnInit?” “What’s the different between Angular’s OnInit versus a constructor?”

Those are valid questions and I’m here to answer them. Before we begin, it’s important to realize that a constructor is not an Angular feature, so keep this in mind.

Let’s cover constructors first then dive into ngOnInit lifecycle hook.

What is a constructor?

The constructor is part of a JavaScript class (or TypeScript class.

The constructor is where we would initialize a new instance and pass data into, if required.

A constructor is called first when a class instance is created:

class Pizza {
  constructor() {
    console.log('Hello world!');
  }
}

// create a new instance, constructor is 
// then invoked by the *JavaScript Engine*!
const pizza = new Pizza();

The JavaScript engine calls the constructor, not Angular, so naturally it is called before any lifecycle hooks will be in Angular.

Constructors in Angular

Constructors in Angular are very important, they are our place to provide dependencies through Dependency Injection. When a class is created, Angular analyzes the constructor and looks for the providers that match the types of the constructor arguments. When it finds them it then binds them to the class.

You’ll likely have done something like this and injected another class into the constructor:

import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({...})
class PizzaComponent {
  constructor(
    private route: ActivatedRoute
  ) {}
}

This will bind ActivatedRoute to the class, making it accessible as part of the component class via this.route.

I like to think of a constructor as the place to wire up your dependencies and leave it as clean as possible. As the constructor is not called by Angular directly, it is not as easy to test.

Not only this, but Angular does not have control over when this constructor is called, which leads us to the addition of the ngOnInit lifecycle hook, which fires as soon as the Angular component is ready.

Lifecycle hooks are just methods on a class that describe key points in time. Some lifecycle hooks that Angular provides are; OnInit, OnDestroy and OnChanges. For the complete list of lifecycle hooks, check out the Lifecycle Hooks docs.

Before we move on, a small bit of trivia for you: when an Angular component is created the OnChanges lifecycle hook actually is called first.

The next lifecycle hook that is called is the OnInit hook.

With this in mind, what does the OnInit lifecycle hook give us and when should we use it?

OnInit Lifecycle Hook

Angular components can grow in size and complexity, which often means we want to inject many dependencies and declare properties and bind observables. A constructor would be an ideal place to inject, but not handle the wiring up.

The lifecycle hooks are invoked by Angular during the change detection cycle, whereas the constructor is only invoked when Angular composes the component tree.

This is where ngOnInit comes in, which we can declare as a method after importing the OnInit interface and using the implements to give us some type safety:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({...})
class PizzaComponent implements OnInit {
  constructor(
    private route: ActivatedRoute
  ) {}
  
  ngOnInit() {
    // subscribe when OnInit fires
    this.route.params.subscribe(params => {
      // now we can do something!
    });
  }
}

This approach makes unit testing easier as we can control the lifecycle hook. The ngOnInit lifecycle hook makes perfect sense as the place to create more complex logic and initializing of the component.

Here’s what technically happens when Angular instantiates our component and invokes ngOnInit:

const instance = new PizzaComponent();

// Angular calls this when it's ready
instance.ngOnInit();

The ngOnInit lifecycle hook is the perfect place to wire up observables, talk to services and make any requests you need.

Another very important aspect of ngOnInit is that it is called by Angular exactly when the component’s @Input() properties are ready:

@Component({...})
class PizzaSingleComponent implements OnInit {
  @Input() pizza: Pizza;

  constructor() {
    console.log(this.pizza); // undefined
  }
  
  ngOnInit() {
    console.log(this.pizza); // { name: 'Pepperoni', price: 399 }
  }
}

This ngOnInit phase also includes the first pass of Angular’s Change Detection, which means any @Input() properties are available inside ngOnInit, however are undefined inside the constructor, by design.

The differences are subtle, yet important. Now we’ve fully explored the constructor role, you’re now primed to make the right decision with OnInit. Using OnInit gives us a guarantee that bindings are readily available and we can use them to the fullest.

🚀 Want to learn even more awesome Angular practices?
Check out my new Angular Courses if you’re serious about taking your skills to the top! There’s so much about Standalone Components and all the new best practices in the course.

Thanks for reading, happy constructing!

Related blogs 🚀

Free eBooks:

Angular Directives In-Depth eBook Cover

JavaScript Array Methods eBook Cover

NestJS Build a RESTful CRUD API eBook Cover