question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

StaticInjector not working in a factory provider

See original GitHub issue

Bug Report

Affected Package

The issue is caused by package @angular/core. It's about DI

Is this a regression?

The issue was not present while using ReflectiveInjector

Description

Creating StaticInjector within a factory provider does not work.

Using injector.create, the resulting injector can inject a class with dependencies when used in a component, but not in a factory provider. given:

@Injectable()
class TestA {
  constructor() {
  }
}

const testAProvider = {provide: TestA, useClass: TestA, deps: []};

@Injectable()
class TestB {
  constructor(private a: TestA) {
  }
}

const testBProvider = {provide: TestB, useClass: TestB, deps: [TestA]};
export function factory(injector: Injector): string {
  const pi = Injector.create({providers: [testAProvider], name: 'TestA'});
  const i = Injector.create({providers: [testBProvider], parent: pi, name: 'TestB'});
  console.log(i.get(TestB));
  return 'hi';
}
@Component({
  selector: 'app-demo',
  templateUrl: './component.html',
  styleUrls: ['./component.scss'],
  providers: [
    {
      provide: 'hi',
      useFactory: factory,
    }
  ]
})

causes an error shown below

but

ngOnInit(): void {
   const pi = Injector.create({providers: [testAProvider], name: 'TestA'});
   const i = Injector.create({providers: [testBProvider], parent: pi, name: 'TestB'});
   console.log(i.get(TestB));
 }

works

image

Minimal Reproduction

https://stackblitz.com/edit/angular-di-bug

Exception or Error

ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(TestB)[TestB -> TestA -> TestA -> TestA -> TestA]: 
  NullInjectorError: No provider for TestA!
NullInjectorError: R3InjectorError(TestB)[TestB -> TestA -> TestA -> TestA -> TestA]: 
  NullInjectorError: No provider for TestA!
    at NullInjector.get (core.js:11081)
    at R3Injector.get (core.js:11247)
    at R3Injector.get (core.js:11247)
    at R3Injector.get (core.js:11247)
    at NgModuleRef$1.get (core.js:25341)
    at R3Injector.get (core.js:11247)
    at NgModuleRef$1.get (core.js:25341)
    at Object.get (core.js:25055)
    at lookupTokenUsingModuleInjector (core.js:3342)
    at getOrCreateInjectable (core.js:3454)
    at resolvePromise (zone.js:1213)
    at resolvePromise (zone.js:1167)
    at zone.js:1279
    at ZoneDelegate.invokeTask (zone.js:406)
    at Object.onInvokeTask (core.js:28661)
    at ZoneDelegate.invokeTask (zone.js:405)
    at Zone.runTask (zone.js:178)
    at drainMicroTaskQueue (zone.js:582)

Your Environment

Angular Version:

Angular CLI: 12.0.5
Node: 14.16.0
Package Manager: npm 7.14.0
OS: darwin x64

Angular: 12.0.5
... animations, cdk, cli, common, compiler, compiler-cli, core
... forms, material, platform-browser, platform-browser-dynamic
... router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1200.5
@angular-devkit/build-angular   12.0.5
@angular-devkit/core            12.0.5
@angular-devkit/schematics      12.0.5
@angular/flex-layout            12.0.0-beta.34
@schematics/angular             12.0.5
ng-packagr                      12.0.8
rxjs                            6.6.7
typescript                      4.2.4

Anything else relevant?

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:1
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
alxhubcommented, Jun 25, 2021

I think the issue is that we end up bypassing the “active injector” in an unintended way.

What’s supposed to happen is:

  1. We start of with the call to R3Injector.get() which sets that injector as active (replacing the component’s injector)
  2. As part of calling the factory to create the requested value, it calls injectArgs to inject its dependencies.
  3. injectArgs calls inject for each dependency
  4. inject requests the dependency from the active injector which we set above.

Unfortunately, there’s a complication here - inject actually allows its implementation to be replaced. Before requesting dependencies, components do this by setting directiveInject as the active injection strategy. When inject is called in step 3 above then, it ends up delegating to directiveInject, which looks up the node injector to use from the current LView, ignoring the injector we set in step 1.

To fix this, I think R3Injector needs to not only set the current injector in step 1, but also clear the current injection strategy if one is set (and restore it at the end, of course, just like the active injector). That way we don’t delegate through to directiveInject and end up reaching the wrong injector.

0reactions
angular-automatic-lock-bot[bot]commented, Aug 20, 2021

This issue has been automatically locked due to inactivity. Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Angular5: Dynamic component loading - StaticInjector error ...
I am migrating an electron application from AngularJS to Angular5 and I am facing issues in a part where I need to load...
Read more >
Configuring dependency providers - Angular
The useFactory field specifies that the provider is a factory function whose implementation is heroServiceFactory . · The deps property is an array...
Read more >
Dependency Injection using Decorators | by Chidume Nnamdi
Angular uses the concept of providers to configure injectors: ... Either from a class useClass or from a value useValue or from a...
Read more >
angulartics/angulartics2 - Gitter
Hello, I see in the 7.2.0 release from npm registry the Launch provider is ... to use the application insights provider but there...
Read more >
Providers in Angular - Scaler Topics
If the dependency is not resolved, it throws a StaticInjector error. Configuring the Angular Provider. There are different ways to configure a ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found