Binge On Code

Jun 13, 2023

62 readers

How To Use New Angular Inject Outside Constructor Phase

Angular just shipped with an awesome way to provide dependency injection on the fly - Angular inject. However, this comes with one key catch, it must be used in constructor phase! Well, let’s see how to do this.

inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with `runInInjectionContext` - angular compiler

This article has three key sections. The first is understanding what Angular inject is. The next will be understanding what the constructor phase means in Angular. Finally, a short code demo on how to go about this and fix the Angular inject constructor phase error you most likely are facing.

Angular inject

Since Angular 14, the Angular inject was a streamlined method for injecting DI tokens, the likes of Services for example. It makes it easy for us Angular engineers, to actually create shareable DI tokes, without the need to create classes.

Simply put, Angular inject offers a few benefits:

  1. Removing code boilerplate.

  2. Shareable functions.

  3. Cleaner code base.

So, how and when should you use Angular inject? Well, it can be used in a couple of contexts, such as factory functions, but for the purpose of this tutorial, we will focus on Angular constructor phase.

What is the constructor phase in Angular

Simply put anything that precedes or is within the constructor of a class. This basically covers all things member attributes of a class, or anything variable that is within the scope of a constructor i.e. instantiated inside a constructor.

Angular inject constructor phase error

Well, now for the issue at hand, why does this happen? Well, now that you know when and how to use Angular inject, you probably have an idea of where to use it now.

You must have been using it within a scope of a component method. Let's see a classical example problem.

Problem

Fix the below code to have it work with inject on click.

 

<button #buttonElement (click)="hideThisElementOnClick(buttonElement)">

 Hide On Click

</button>

 

@Component({

 selector: 'app-root',

 templateUrl: './app.component.html',

 styleUrls: ['./app.component.scss'],

})

export class AppComponent {

 title = 'playground';

 public hideThisElementOnClick(element: HTMLElement): void {

   const renderer2 = inject(Renderer2);

 }

}

 

Well, this will not work, but why? Well, the section where renderer2 is injected is not within the constructor phase (which is what we are focusing on in this piece)

But how can you tap into the constructor phase? Well, JavaScript closures! But what is a JavaScript closure? This is a story for another day, but I will tell you this, it creates for use a constructor scope that we can work with

So, let’s change our code

Fixed

 

import { Component, inject, Renderer2 } from '@angular/core';


export const hideElement = () => {

 const renderer2: Renderer2 = inject(Renderer2);

 return (element: HTMLElement) => {

   renderer2.setProperty(element, 'hidden', true);

 };

};


@Component({

 selector: 'app-root',

 templateUrl: './app.component.html',

 styleUrls: ['./app.component.scss'],

})

export class AppComponent {

 private _hideElement = hideElement();


 public hideThisElementOnClick(element: HTMLElement): void {

   this._hideElement(element);

 }

}

 

What is happening?

So, why does this code work? Well, let’s see.

 

export const hideElement = () => {

 const renderer2: Renderer2 = inject(Renderer2);

 return (element: HTMLElement) => {

   renderer2.setProperty(element, 'hidden', true);

 };

};

 

This is our closure function. It will create for us an DI token for Renderer2, then return for us a function which we can use. In this inner function, we have access to both the outer and the inner scope, meaning, we can call the hideElement from constructor phase and still have a reference to the DI token for Renderer2

 

private _hideElement = hideElement();

 

Now, here, is where we call hideElement() getting a reference to the closure scope, which will be available and ready when we need it.

 

  public hideThisElementOnClick(element: HTMLElement): void {

    this._hideElement(element);

  }

 

Finally, we can call the inner scope of hideElement() with the needed parameter!

And that is it!

This is a really awesome effort by Angular, so please share it with your friends!

Happy coding!

Related Articles

A Practical Example To Validate Angular Input Fields With Joi

At times, we want to do a quick and custom validation of Angular inputs. Well, how let’s see how we can validate Angular input fields using Joi.

Jun 21, 2023

Views 16

Angular Fix MatFormField Must Contain A MatFormFieldControl

Whenever we are using Angular material, mostly Angular mat-form-field, we get the error that mat-form-field must contain a mat-form-field-control, well, let’s fix that.

Jun 27, 2023

Views 13

Have You Tried Angular Switch Yet

Angular 17 shipped with Angular @switch, and this was a game changer, when it comes to simplicity compared to the former ngSwitch directive. In this guide, we will see how easy it is to use this awesome addition to Angular!

Nov 19, 2024

Views 29

Angular JavaScript React CSS Django Python React Native Next JS State Management Django Rest Framework Unity