Hack Frontend Community

Иерархия инжекторов в Angular

Что такое иерархия инжекторов в Angular?

В Angular внедрение зависимостей (Dependency Injection, DI) построено на иерархии инжекторов, где зависимости могут быть доступны:

  • Глобально
  • Только внутри модуля
  • Только в компоненте
  • Только в конкретной директиве или pipe

Angular создает дерево инжекторов, схожее с деревом компонентов. Каждый компонент может иметь собственный инжектор, унаследованный от родителя.

Уровни иерархии инжекторов

Глобальный уровень — @Injectable({ providedIn: 'root' })

  • Сервис создаётся один раз на всё приложение.
  • Доступен везде без необходимости указывать в providers.
@Injectable({ providedIn: 'root' })
export class LoggerService {}

Используется по умолчанию для singleton-сервисов.

Модульный уровень (в @NgModule.providers)

  • Сервис доступен только в пределах указанного модуля.
  • Если модуль импортируется в другие модули — поведение может отличаться.
@NgModule({
  providers: [AuthService]
})
export class AuthModule {}

Хорошо для ограниченного доступа и lazy-loaded модулей.

Компонентный уровень (в @Component.providers)

  • Сервис создаётся отдельно для каждого экземпляра компонента.
  • Подходит для локального состояния, независимого от других компонентов.
@Component({
  selector: 'app-cart',
  templateUrl: './cart.component.html',
  providers: [CartService]
})
export class CartComponent {}

При каждом использовании компонента будет создан новый экземпляр сервиса.

Уровень директив/пайпов (@Directive.providers)

  • Аналогично компонентам — сервис создаётся локально, внутри элемента, к которому применяется директива.
@Directive({
  selector: '[highlight]',
  providers: [HighlightService]
})
export class HighlightDirective {}

Как Angular ищет зависимости

Когда Angular внедряет зависимость, он:

  • Сначала смотрит в локальном инжекторе компонента.
  • Если не находит — ищет в родительском инжекторе.
  • И так до корневого инжектора (root).
  • Если зависимость не найдена — выбрасывает ошибку.

Пример: вложенные компоненты и разные инжекторы

@Component({
  selector: 'parent',
  providers: [SharedService],
  template: `<child></child>`
})
export class ParentComponent {}

@Component({
  selector: 'child',
  template: `...`
})
export class ChildComponent {
  constructor(shared: SharedService) {} // получит instance от parent
}

Если SharedService не указан в child, он будет унаследован от родителя.