0

In an Angular app I need to show a popup message when a remote module fails to load (e.g. the target micro frontend is offline).

As the text is localized, I'd try to use the inject() method in the "field initializer" case to create an instance of the TranslatePipe.

const handleLoadError = () => {
  const translatePipe = inject(TranslatePipe); // <---

  const targetData = {
    content: translatePipe.transform('MESSAGE')
  };

  // Show popup ...
}

const routes: Routes = [
  {
    path: 'home',
    loadChildren: () => loadRemoteModule('targetUrl/home.js')
    .then((m) => m.AppModule)
    .catch(() => {
      handleLoadError();
    })
  }
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes),
  ],
  exports: [RouterModule],
})
export class AppRoutingModule {}

However, it seems this is not accepted as I get the following error:

Error: NG0203: inject() must be called from an injection context such as a
constructor, a factory function, a field initializer, or a function used with
`EnvironmentInjector#runInContext`.

UPDATED
After debugging the framework, I could see that the exception occurs in the Angular core.js file in the following part. In my case, _currentInjector is not set and triggers an exception.

/**
 * Current injector value used by `inject`.
 * - `undefined`: it is an error to call `inject`
 * - `null`: `inject` can be called but there is no injector (limp-mode).
 * - Injector instance: Use the injector for resolution.
 */
let _currentInjector = undefined;
function setCurrentInjector(injector) {
    const former = _currentInjector;
    _currentInjector = injector;
    return former;
}
function injectInjectorOnly(token, flags = InjectFlags.Default) {
    if (_currentInjector === undefined) {
        throw new RuntimeError(-203 /* RuntimeErrorCode.MISSING_INJECTION_CONTEXT */, ngDevMode &&
            `inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with \`EnvironmentInjector#runInContext\`.`);
    }
    //...
Francesco
  • 9,947
  • 7
  • 67
  • 110
  • Does this answer your question? [inject() must be called from an injection context](https://stackoverflow.com/questions/51485868/inject-must-be-called-from-an-injection-context) – Vikas Jun 05 '23 at 16:39
  • Not really, even if the title sounds like the same error. Looking to code examples my case seems to fall in the "field initializer" one. However, by debugging the Angular code, I see that there is no provider available, and this triggers the exception (details in the updated section). – Francesco Jun 05 '23 at 20:20
  • Googling the error message finds a [lengthy description of this error](https://angular.io/errors/NG0203) in the official angular docs, including both its causes and a suggested remedy. I am not sure what we could add to that, as it seems quite comprehensive? – meriton Jun 05 '23 at 20:39
  • You have to call inject() after angular has started or before startup in specific places where angular expects you to do it. Read https://angular.io/errors/NG0203 for more explanations. – serrulien Jun 05 '23 at 21:11
  • Where is TranslatePipe provided ? – serrulien Jun 05 '23 at 21:17
  • TranslatePipe comes from the ngx-translate library. Looking at the usage scope, mine seems to fall into a factory function or field initializer. I have seen examples with the same code structure. Here what's probably special is that the inject function is called inside the routing module in the case of an error. – Francesco Jun 06 '23 at 05:32

1 Answers1

0

Moving the inject() method call into an injectable service makes it work. Calling it inside a function in the routes array did not provide the needed context, apparently.

@Injectable({
  providedIn: 'root'
})
export class PopupServiceService {
  translatePipe = inject(TranslatePipe); // <-- This works

  showPopup() {
     const targetData = {
         content: translatePipe.transform('MESSAGE')
     };

     // Show popup...
  }
}

In app-routing.module.ts

const routes: Routes = [
  {
    path: 'home',
    loadChildren: () => loadRemoteModule('targetUrl/home.js')
    .then((m) => m.AppModule)
    .catch(() => popupService.showPopup())
  }
];
Francesco
  • 9,947
  • 7
  • 67
  • 110