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

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

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

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

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

5 ответов


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

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

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

по поводу ваших вопросов:

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

посмотреть эти вопросы для получения дополнительной информации:


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


@Injectable ()

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

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

bootstrap (приложение, [сервис])

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

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

поставщики: [сервис]

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

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

@Inject ()

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

Более Дальнеишее Чтение

надеюсь, что это помогает. :)


мне нужно использовать операторы: []

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

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


> =RC.5

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

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

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

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


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

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

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

@Inject ()

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

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 {}

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

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

constructor(formBuilder: FormBuilder) {}

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

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

Почему @Injectable ()?

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

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

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

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

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

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

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

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