Подтвердить что ты не робот

Как правильно использовать инъекцию зависимостей (DI) в Angular2?

Я пытаюсь выяснить, как работает инъекция зависимостей (DI) в Angular2. Я сталкивался с множеством проблем/проблем каждый раз, когда я пытался внедрить сервис/класс в мои компоненты.

Из разных статей в googled мне нужно либо использовать поставщиков: [] в конфигурации Component, либо иногда мне нужно использовать @Inject() в моем конструкторе или вводить непосредственно в bootstrap (приложение, [услуга])? Я также видел, как некоторые статьи хотят, чтобы я поставил @indjectable decorator.

Например: для ввода Http мне нужно импортировать {Http} и помещать Http в провайдеры, но для FormBuilder мне нужно использовать @Inject() в конструкторе.

Есть ли какое-либо эмпирическое правило о том, когда использовать что? Не могли бы вы предоставить несколько примеров кода? Спасибо: -)

4b9b3361

Ответ 1

Включение зависимостей в Angular2 зависит от иерархических инжекторов, которые связаны с деревом компонентов.

Это означает, что вы можете настроить поставщиков на разных уровнях:

  • Для всего приложения при его загрузке. В этом случае все вспомогательные инжекторы (компонентные) будут видеть этого провайдера и совместно использовать связанный с ним экземпляр. При взаимодействии он будет тем же самым экземпляром
  • Для конкретного компонента и его подкомпонентов. То же, что и раньше, но для конкретного компонента. Другие компоненты не будут видеть этого провайдера. Если вы переопределите что-то определенное выше (например, при начальной загрузке), этот поставщик будет использоваться вместо этого. Таким образом, вы можете переопределить все.
  • Для служб. С ними нет провайдеров. Они используют те из инжектора из элемента, который запускает (прямо = компонент или косвенно = компонент, который вызывает вызов цепочки обслуживания).

Относительно ваших других вопросов:

  • @Injectable. Чтобы ввести в класс, вам нужен декоратор. Компоненты имеют один (@Component один), но услуги - простые классы. Если для службы требуется, чтобы в нее были введены зависимости, вам нужен этот декоратор.
  • @Inject. В большинстве случаев тип параметров конструктора достаточно, чтобы Angular2 определял, что вводить. В некоторых случаях (например, если вы явно используете OpaqueToken, а не класс для регистрации поставщиков), вам нужно указать некоторые подсказки о том, что нужно вводить. В таких случаях вам нужно использовать @Inject.

Дополнительные сведения см. в этих вопросах:

Ответ 2

Широкий вопрос, TL; DR версия


@Injectable()

  • является декоратором, который сообщает typescript, что декорированный класс имеет dependencies и не означает, что этот класс может быть введен каким-либо другим.

  • И затем TypeScript понимает, что при построении с помощью зависимостей imported необходимо вставить требуемые метаданные в декорированный класс.

bootstrap (приложение, [услуга])

  • bootstrap() заботится о создании инжектора корней для нашего приложения при его загрузке. Он принимает список поставщиков как второй аргумент, который будет передаваться непосредственно инжектору, когда он будет создан.

  • Вы загрузите свое приложение с помощью служб, которые будут использоваться во многих местах, таких как Http, что также означает, что вам не нужно писать providers: [Http] в вашей конфигурации класса.

провайдеры: [услуга]

  • Поставщики
  • также выполняют работу по передаче всех аргументов служб Injector.

  • Вы предоставляете услуги провайдерам, если это не bootstrap(). И нужно только в нескольких местах.

@Inject()

  • является также декоратором функцией, которая выполняет работу по фактическому внедрению этих служб
    как это. constructor(@Inject(NameService) nameService)
  • но если вы используете TS все, что вам нужно сделать, это constructor(nameService: NameService) и TypeScript будут обрабатывать остальные.

Дополнительная литература

Надеюсь, это поможет.:)

Ответ 3

Мне нужно либо использовать поставщиков: []

Для инъекции зависимостей, чтобы иметь возможность создавать экземпляры для вас, вам необходимо зарегистрировать поставщиков для этих классов (или других значений) где-нибудь.

При регистрации поставщика определяет область действия созданного значения. Угловые DI являются иерархическими. Если вы зарегистрируете провайдера в корне дерева


>= RC.5

@NgModule({
  providers: [/*providers*/]
  ...
})

или для ленивых загружаемых модулей

static forRoot(config: UserServiceConfig): ModuleWithProviders {
  return {
    ngModule: CoreModule,
    providers: [
      {provide: UserServiceConfig, useValue: config }
    ]
  };
}

<= rc.4

(bootstrap(AppComponent, [Providers}) или @Component(selector: 'app-component', providers: [Providers]) (корневой компонент)


тогда все компоненты и службы, запрашивающие экземпляр, получают один и тот же экземпляр.

Если поставщик зарегистрирован в одном из дочерних компонентов, для потомков этого компонента предоставляется новый (другой) экземпляр.

Если компонент запрашивает экземпляр (по параметру конструктора), DI смотрит "вверх" дерево компонентов (начиная с листа по направлению к корню) и берет первого найденного им поставщика. Если экземпляр для этого провайдера уже был создан ранее, этот экземпляр используется, в противном случае создается новый экземпляр.

@Inject()

Когда компонент или служба запрашивает значение от DI, например

constructor(someField:SomeType) {}

DI ищет поставщика по типу SomeType. Если @Inject(SomeType) добавлен

constructor(@Inject(SomeType) someField:SomeType) {}

DI ищет поставщика по параметру, переданному в @Inject(). В приведенном выше примере параметр, переданный в @Inject(), совпадает с типом параметра, поэтому @Inject(SomeType) является избыточным.

Однако есть ситуации, когда вы хотите настроить поведение, например, для ввода параметров конфигурации.

constructor(@Inject('someName') someField:string) {}

Тип string недостаточен для того, чтобы различать определенный параметр конфигурации, когда у вас несколько зарегистрированных.
Значение конфигурации должно быть зарегистрировано как поставщик где-то вроде


>= RC.5

@NgModule({
  providers: [{provide: 'someName', useValue: 'abcdefg'})]
  ...
})
export class AppModule {}

<= rc.4

bootstrap(AppComponent, [provide('someName', {useValue: 'abcdefg'})])

Для этого вам не нужно @Inject() для FormBuilder, если конструктор выглядит как

constructor(formBuilder: FormBuilder) {}

Ответ 4

Я добавлю несколько вещей, которые я не видел в других ответах. (В то время, когда я пишу это, это означает ответы Тьерри, Гюнтера и А_Синга).

  • Всегда добавляйте Injectable() к создаваемым вами службам. Хотя это необходимо только в том случае, если ваш сервис сам должен что-то вводить, рекомендуется всегда включать его.
  • Массив providers в директивах/компонентах и ​​массив providers в NgModules - это только два способа регистрации поставщиков, которые не встроены. (Примеры встроенных объектов, которые нам не нужно регистрировать, это ElementRef, ApplicationRef и т.д. Мы можем просто вставить их.)
  • Когда компонент имеет массив providers, тогда этот компонент получает инжектор Angular. Инжектора консультируются, когда что-то хочет ввести зависимость (как указано в конструкторе). Мне нравится думать о дереве инжектора как о сокращенном дереве, чем о дереве компонентов. Первый инжектор, который может удовлетворить запрос зависимости, делает это. Эта иерархия инжекторов позволяет зависимостям быть одноточечными или нет.

Ответ 5

Почему @Injectable()?

@Injectable() отмечает класс, доступный инжектору для создания экземпляра. Вообще говоря, инжектор сообщит об ошибке при попытке создать экземпляр класса, который не помечен как @Injectable().

Как бы то ни было, мы могли бы исключить @Injectable() из нашей первой версии HeroService, потому что у нее не было введенных параметров. Но мы должны иметь это сейчас, когда у нашего сервиса есть инъекционная зависимость. Нам это нужно, потому что Angular требует метаданных параметров конструктора, чтобы ввести Logger.

ПРЕДЛОЖЕНИЕ: ADD @INJECTABLE() ДЛЯ КАЖДОГО СЕРВИСНОГО КЛАССА Мы рекомендуем добавлять @Injectable() к каждому классу обслуживания, даже к тем, у которых нет зависимостей и, следовательно, технически не требуется. Вот почему:

Будущая коррекция: не нужно помнить @Injectable(), когда мы добавим зависимость позже.

Согласованность: все службы соответствуют тем же правилам, и нам не нужно задаваться вопросом, почему отсутствует декоратор.

Инъекторы также несут ответственность за создание экземпляров компонентов, таких как HeroesComponent. Почему мы не отметили HeroesComponent как @Injectable()?

Мы можем добавить его, если хотим. Это не обязательно, потому что HeroesComponent уже отмечен с помощью @Component, и этот класс декоратора (например, @Directive и @Pipe, о которых мы узнаем позже) является подтипом InjectableMetadata. На самом деле это декораторы InjectableMetadata, которые идентифицируют класс как цель для инстанцирования инжектором.

Источник: https://angular.io/docs/ts/latest/guide/dependency-injection.html