Внести вклад
Подробные руководства
Внедрение зависимостей

Внедрение зависимостей в Angular

Внедрение зависимостей (DI) — это паттерн проектирования, с помощью которого вы организуете и переиспользуете код в приложении: зависимости передаются классу извне, а не создаются внутри него.

СОВЕТ: Перед этим подробным руководством ознакомьтесь с разделом Основы.

По мере роста приложения разработчикам часто нужно переиспользовать функциональность в разных частях кодовой базы. Внедрение зависимостей (DI) помогает в этом: зависимости предоставляются классу вместо создания их напрямую внутри него. Так части приложения становятся более переиспользуемыми и проще в сопровождении.

Внедрение зависимостей популярно, потому что помогает решать типичные задачи:

  • Улучшенная сопровождаемость кода: DI способствует чёткому разделению ответственности, упрощает рефакторинг и снижает дублирование.
  • Масштабируемость: модульную функциональность можно переиспользовать в разных частях приложения.
  • Удобнее тестирование: в unit-тестах вместо реальных реализаций можно подставлять тестовые двойники.

Как работает внедрение зависимостей в Angular?

Зависимость — это любой объект, значение, функция или сервис, которые нужны классу для работы, но которые он не создаёт сам. Их предоставляют извне, формируя явные связи между частями приложения.

С системой внедрения зависимостей вы взаимодействуете двумя основными способами:

  • Можно предоставить (provide) значения — сделать их доступными.
  • Можно внедрить (inject) эти значения как зависимости.

В этом контексте «значения» — любые значения JavaScript: объекты, функции или экземпляры классов. Типичные внедряемые зависимости:

  • Конфигурационные значения: константы окружения, URL API, feature flags и т.п.
  • Фабрики: функции, создающие объекты или значения в зависимости от условий во время выполнения
  • Сервисы: классы с общей функциональностью, бизнес-логикой или состоянием

Компоненты и директивы Angular автоматически участвуют в DI: в них можно внедрять зависимости и делать их доступными для внедрения.

Что такое сервисы?

Сервис Angular — это класс TypeScript с декоратором @Service, экземпляр которого можно внедрять как зависимость. Сервисы — самый распространённый способ делиться данными и функциональностью в приложении.

Типичные виды сервисов:

  • Клиенты данных: абстрагируют запросы к серверу для получения и изменения данных
  • Управление состоянием: определяют состояние, общее для нескольких компонентов или страниц
  • Аутентификация и авторизация: управляют входом пользователя, хранением токенов и контролем доступа
  • Логирование и обработка ошибок: задают общий API для логов или сообщения об ошибках пользователю
  • Обработка и рассылка событий: обрабатывают события, не привязанные к конкретному компоненту, или рассылают уведомления компонентам по паттерну наблюдателя
  • Утилиты: переиспользуемые функции форматирования, валидации или вычислений

В следующем примере объявлен сервис AnalyticsLogger:

import {Service} from '@angular/core';

@Service()
export class AnalyticsLogger {
  trackEvent(category: string, value: string) {
    console.log('Analytics event logged:', {
      category,
      value,
      timestamp: new Date().toISOString(),
    });
  }
}

ПРИМЕЧАНИЕ: @Service делает сервис доступным во всём приложении как синглтон. Это рекомендуемый подход для большинства сервисов.

ПОЛЕЗНО: Декоратор @Service — удобный сокращённый вариант для @Injectable({providedIn: 'root'}).

Внедрение зависимостей с помощью inject()

Зависимости можно внедрять функцией Angular inject().

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

import {Component, inject} from '@angular/core';
import {Router} from '@angular/router';
import {AnalyticsLogger} from './analytics-logger';

@Component({
  selector: 'app-navbar',
  template: `<a href="#" (click)="navigateToDetail($event)">Detail Page</a>`,
})
export class Navbar {
  private router = inject(Router);
  private analytics = inject(AnalyticsLogger);

  navigateToDetail(event: Event) {
    event.preventDefault();
    this.analytics.trackEvent('navigation', '/details');
    this.router.navigate(['/details']);
  }
}

Где можно вызывать inject()?

Зависимости можно внедрять при создании компонента, директивы или сервиса. Вызов inject допустим в constructor или в инициализаторе поля. Типичные примеры:

@Component(/* ... */)
export class MyComponent {
  // ✅ In class field initializer
  private service = inject(MyService);

  // ✅ In constructor body
  private anotherService: MyService;

  constructor() {
    this.anotherService = inject(MyService);
  }
}
@Directive({...})
export class MyDirective {
  // ✅ In class field initializer
  private element = inject(ElementRef);
}
import {Service, inject} from '@angular/core';
import {HttpClient} from '@angular/common/http';

@Service()
export class MyService {
  // ✅ In a service
  private http = inject(HttpClient);
}
export const authGuard = () => {
  // ✅ In a route guard
  const auth = inject(AuthService);
  return auth.isAuthenticated();
};

Angular использует термин «контекст внедрения» (injection context) для любого места в коде, где можно вызвать inject. Чаще всего это создание компонента, директивы или сервиса; подробнее см. контексты внедрения.

Дополнительно см. документацию API inject.

Следующие шаги

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

Следующее руководство, Создание и использование сервисов, покажет:

  • Как создать сервис через Angular CLI или вручную
  • Как работает паттерн providedIn: 'root'
  • Как внедрять сервисы в компоненты и другие сервисы

Это покрывает самый распространённый сценарий использования сервисов в приложениях Angular.