Как внедрить фабричную зависимость в Angular фабричный метод провайдера - PullRequest
0 голосов
/ 05 мая 2020

У меня есть проект Angular 9 (9.1.4), и есть собственный поставщик фабрики для BASE_URL (проект основан на. NET Core Angular шаблоне)

// main.ts

export function getBaseUrl() {
  return document.getElementsByTagName('base')[0].href;
}

const providers = [
  { provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] }
];

platformBrowserDynamic(providers).bootstrapModule(AppModule)
  .catch(err => console.error(err));

Если я хочу использовать BASE_URL в классе, это очень просто, я могу просто попросить его ввести в конструктор

export class SomeService {

  constructor(
    private _httpClient: HttpClient,
    @Inject('BASE_URL') private _baseUrl: string  // here we go
  ) { }

, и он будет работать должным образом. Пока все хорошо ...

Недавно я добавил APP_INITIALIZER в приложение, так что я могу загрузить некоторую конфигурацию с сервера заранее. Это делается путем регистрации поставщика с фабричной функцией, в которую также могут быть внедрены зависимости.

Однако простой подход с использованием @Inject по какой-то причине там не работает:

// app.module.ts
@NgModule({
  // other stuff
  providers: [
    SomeService, // here the @Inject works
    {
      provide: APP_INITIALIZER,
      useFactory: (httpClient: HttpClient, @Inject('BASE_URL') baseUrl: string) => {
        // httpClient is OK, but baseUrl is undefined
        // app init implementation
      },
      deps: [HttpClient],
      multi: true
    }],


потому что baseUrl не определен во время выполнения.

Единственный способ заставить его работать - это ввести инжектор из @ angular / core:

{
  provide: APP_INITIALIZER,
  useFactory: (httpClient: HttpClient, injector: Injector) => {
    cont baseUrl = injector.get('BASE_URL'); // works, but deprecated
    // app init implementation
  },
  deps: [HttpClient, Injector],
  multi: true
}

, но проблема заключается в предупреждении об устаревании on метод get (не рекомендуется, начиная с версии 4.0.0).

Как правильно заставить такую ​​заводскую инъекцию работать?

1 Ответ

0 голосов
/ 06 мая 2020

Можно указать желаемое значение так же, как вы это делаете с HttpClient.

Поскольку значение представляет собой простую строку, требуется настраиваемый InjectionToken :

base-url.ts (я использовал отдельный файл, чтобы избежать циклической зависимости)

import { InjectionToken } from "@angular/core";

export const BASE_URL = new InjectionToken<string>('BaseUrl');

export function getBaseUrl() {
  return document.getElementsByTagName('base')[0].href;
}

app.module.ts

import { BASE_URL } from "../base-url";
// ...
  providers: [{
    provide: APP_INITIALIZER,
    useFactory: (httpClient: HttpClient, baseUrl: string) => {
      return () => {
        console.log(baseUrl);
        return Promise.resolve();
      };
    },
    deps: [HttpClient, BASE_URL], // <-- Here
    multi: true
  }],
// ...

Edit
Да, на самом деле, строку можно использовать как токен, поэтому добавляйте ее как второе значение to deps массив помогает. Первоначальная проблема заключалась в том, что невозможно украсить аргументы функции (не метода), и поэтому в случае внедрения фабрики используется массив deps.
Итак, это работает:

  providers: [{
    provide: APP_INITIALIZER,
    useFactory: (httpClient: HttpClient, baseUrl: string) => {
      return () => {
        console.log(baseUrl);
        return Promise.resolve();
      };
    },
    deps: [HttpClient, 'BASE_URL'], // <-- Here
    multi: true
  }],

Но я бы посоветовал использовать вместо него InjectionToken, потому что это безопаснее. Также обратите внимание, что предупреждение об устаревании, которое у вас есть для injector.get('BASE_URL');, относится к строковым токенам, поскольку только подпись get, которая принимает укусы, обесценивается .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...