properly understanding inject in angular14 - inject() must be called from an injection context

Viewed 1304

I'm trying to learn the changes in angular 14, especially the inject() feature where i'm able to inject modules to functions and i don't need to create special services for that.. but i think i got something wrong.

I'm trying to create some static functions to send snack messages using the package ngx-toastr, but this package is not relevant to my question. how do I properly implement functions that show snack messages while injecting to them the required modules that they need to operate.

this is my messages.ts file:

import {inject} from '@angular/core';
import {ToastrService} from 'ngx-toastr';


export const snackMsgSuccess = (msg: string, title?: string) => {
  const toaster = inject(ToastrService);
  toaster.success(msg, title, {
    easeTime: 1000
  });
};


export const snackMsgInfo = (msg: string, title?: string) => {
  const toaster = inject(ToastrService);
  toaster.info(msg, title, {
    easeTime: 1000
  });
};

export const snackMsgWarn = (msg: string, title?: string) => {
  const toaster = inject(ToastrService);
  toaster.warning(msg, title, {
    easeTime: 1000
  });
};


export const snackMsgError = (msg: string, title?: string) => {
  const toaster = inject(ToastrService);
  toaster.error(msg, title, {
    easeTime: 1000
  });
};

and I got the following error:

Error: Uncaught (in promise): Error: NG0203: inject() must be called from an injection context (a constructor, a factory function or a field initializer)

well... i had a problem before when i tried to have a supporting function to get route params:

export const routeParam$ = (key: string) => {
  const activatedRoute = inject(ActivatedRoute);

  return activatedRoute.params.pipe(
    pluck(key),
    filter(r => r !== null),
    distinctUntilChanged()
  );
};

and i was only able to use as an field initializer in a component with task: Observable<string> = routeParam$('task');

well the error message is very clear... but still.. i'm new to angular14 and i thought that inject would allow me to do that. it's not that useful for me otherwise.

for now I moved it as a service..

import {Injectable} from '@angular/core';
import {ToastrService} from 'ngx-toastr';

@Injectable({
  providedIn: 'root'
})
export class MsgService {

  constructor(private toaster: ToastrService) {
  }

  public snackMsgSuccess = (msg: string, title?: string) => {
    this.toaster.success(msg, title, {
      easeTime: 1000
    });
  };


  public snackMsgInfo = (msg: string, title?: string) => {
    this.toaster.info(msg, title, {
      easeTime: 1000
    });
  };

  public snackMsgWarn = (msg: string, title?: string) => {
    this.toaster.warning(msg, title, {
      easeTime: 1000
    });
  };


  public snackMsgError = (msg: string, title?: string) => {
    this.toaster.error(msg, title, {
      easeTime: 1000
    });
  };
}

but is this the only way to implement it ? even in angular14 ?

1 Answers

As mentioned in the Answer, It can be only initialised during instantiation of a dependency by the DI system. You can workaround this by creating higher order function.

export const snackMsgSuccess = () => {
  const toaster = inject(ToastrService);
  return (msg: string,title?: string)=>{
    toaster.success(msg, title, {
      easeTime: 1000
    });
  }
};

component.ts

snackMsgSuccess = snackMsgSuccess();


ngOnInit(){
   this.snackMsgSuccess('Success','Test');
}

Update Angular 14.1

In this version inject function can be used inside function body using runInContext API.

For More Info

Related