Понимание контекста Angular Injection

May 02 2023
В последних версиях Angular появилась функция inject(), которая позволяет нам получить ссылку на провайдера функциональным способом, а не с помощью метода Injector.get().

Angular представил эту inject()функцию в последних версиях, которая позволяет нам получить ссылку на провайдера функциональным способом, а не с помощью метода Injector.get(). Однако, если вы использовали его или библиотеку, которая использует его под капотом, вы могли столкнуться со следующей ошибкой:

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 есть две глобальные переменные, которые могут хранить injectorв определенный момент времени: одна для Injectorи одна для NodeInjector. Вот фрагменты кода для обоих:

let _currentInjector = undefined;

export function getCurrentInjector() {
  return _currentInjector;
}

export function setCurrentInjector(injector: Injector|undefined|null {
  const former = _currentInjector;
  _currentInjector = injector;
  return former;
}
let _injectImplementation

export function getInjectImplementation() {
  return _injectImplementation;
}

export function assertInInjectionContext(debugFn: Function): void {
  if (!getInjectImplementation() && !getCurrentInjector()) {
    throw new RuntimeError(
        RuntimeErrorCode.MISSING_INJECTION_CONTEXT,
        ngDevMode &&
            (debugFn.name +
             '() can only be used within an injection context such as a constructor, a factory function, a field initializer, or a function used with `runInInjectionContext`'));
  }
}

import {
  ElementRef,
  assertInInjectionContext,
  inject,
} from '@angular/core';

export function injectNativeElement<T extends Element>(): T {
  assertInInjectionContext(injectNativeElement);

  return inject(ElementRef).nativeElement;
}

  • В factoryфункции, указанной для InjectionToken:
  • export const FOO = new InjectionToken('FOO', {
      factory() {
        const value = inject(SOME_PROVIDER);
        return ...
      },
    });
    

    
    @Component({
      providers: [
        {
          provide: FOO,
          useFactory() {
            const value = inject(SOME_PROVIDER);
            return ...
          },
        },
      ]
    })
    export class FooComponent {}
    

    @Component({})
    export class FooComponent {
      foo = inject(FOO);
    
      constructor(private ngZone: NgZone = inject(NgZone)) {
        const bar = inject(BAR);
      }
    }
    

  • Внутри функции, которая выполняется с помощью runInInjectionContextфункции:
  • import { runInInjectionContext } from '@angular/core';
    
    export class FooComponent {
      ngOnInit() {
        runInInjectionContext(this.injector, () => {
          console.log(
            'I can access the NodeInjector using inject()',
            inject(ElementRef)
          );
        })
    }
    

Подпишитесь на меня в Medium или Twitter , чтобы узнать больше об Angular и JS!