
Декоратор @Injectable в Angular определяет класс как сервис, который можно внедрять в компоненты и другие сервисы через механизм Dependency Injection. Он позволяет Angular автоматически создавать и управлять экземплярами сервисов без ручного создания объектов с помощью оператора new.
Сервисы с @Injectable могут быть зарегистрированы на уровне модуля или корневого инжектора через параметр providedIn. Например, providedIn: ‘root’ гарантирует, что Angular создаст один глобальный экземпляр сервиса на весь проект, а указание конкретного модуля позволяет ограничить область действия сервиса только этим модулем.
Использование @Injectable упрощает повторное применение кода и управление состоянием приложения. Компоненты получают сервисы через конструктор, что делает зависимости явными и легко отслеживаемыми. Такой подход снижает дублирование логики и облегчает поддержку приложения при масштабировании.
Кроме того, @Injectable обеспечивает возможность создавать сервисы с несколькими экземплярами, если это необходимо для отдельных модулей или компонентов, а также облегчает тестирование, позволяя подменять реальные сервисы на mock-объекты в unit-тестах.
Неправильная конфигурация или отсутствие @Injectable может приводить к ошибкам типа NullInjectorError. Знание того, как и где регистрировать сервис, позволяет избежать проблем при внедрении зависимостей и делает архитектуру приложения более предсказуемой.
Injectable в Angular: как использовать и для чего нужен

Декоратор @Injectable делает класс доступным для системы Dependency Injection Angular. Без него Angular не сможет автоматически создавать экземпляры сервиса, что приведет к ошибкам при внедрении в компоненты или другие сервисы.
Для использования сервиса с @Injectable достаточно зарегистрировать его через параметр providedIn. Например, providedIn: ‘root’ создаст один глобальный экземпляр сервиса для всего приложения, а указание конкретного модуля ограничит область действия сервиса рамками этого модуля.
Сервисы внедряются в компонент через конструктор: Angular автоматически передает нужный экземпляр при создании компонента. Это упрощает управление состоянием, так как все зависимости объявлены явно и не требуют ручного создания объектов.
При необходимости создавать отдельные экземпляры сервиса для разных модулей или компонентов можно комбинировать @Injectable с провайдерами на уровне модулей. Такой подход позволяет контролировать жизненный цикл и область действия сервисов без изменения их внутренней логики.
Использование @Injectable облегчает тестирование: сервисы можно подменять mock-объектами или spy-объектами, не меняя код компонентов. Это особенно важно при юнит-тестах и интеграционных тестах, где требуется изолировать зависимости.
Ошибки типа NullInjectorError чаще всего возникают при попытке внедрить сервис без @Injectable или при неправильной регистрации через providedIn. Четкое понимание механизма позволяет избежать таких проблем и строить предсказуемую архитектуру приложения.
Что делает декоратор @Injectable в Angular

Декоратор @Injectable сообщает Angular, что класс предназначен для внедрения зависимостей и может быть создан через механизм Dependency Injection. Без него Angular не сможет определить, какие зависимости необходимы для конструктора сервиса.
@Injectable позволяет использовать параметр providedIn, который управляет областью видимости сервиса. Значение ‘root’ создает один глобальный экземпляр для всего приложения, а указание конкретного модуля ограничивает создание сервиса рамками этого модуля, предотвращая ненужное дублирование объектов.
Кроме регистрации через providedIn, декоратор @Injectable формирует метаданные, которые Angular использует для анализа зависимостей при компиляции. Это позволяет внедрять другие сервисы в конструктор текущего класса без ручного создания экземпляров.
Использование @Injectable также обеспечивает возможность lazy-инстанцирования сервисов: экземпляр создается только при первом обращении, что уменьшает потребление памяти и ускоряет загрузку приложения.
Без @Injectable Angular выдаст NullInjectorError при попытке внедрить сервис, даже если он зарегистрирован в модуле. Правильное применение декоратора гарантирует корректное создание и управление жизненным циклом сервисов.
Как зарегистрировать сервис через providedIn
Регистрация сервиса через providedIn в Angular позволяет управлять областью видимости и жизненным циклом экземпляров. Этот способ заменяет ручное добавление сервиса в массив providers модуля.
Чтобы зарегистрировать сервис, в декораторе @Injectable укажите объект с ключом providedIn:
- Глобальный сервис: providedIn: ‘root’ создаст один экземпляр на все приложение.
- Локальный сервис для модуля: providedIn: SomeModule ограничивает сервис рамками конкретного модуля.
- Lazy-сервис: сервис создается только при первом обращении, если он зарегистрирован через providedIn.
Пример синтаксиса:
@Injectable({ providedIn: ‘root’ })
export class MyService {}
При таком подходе Angular автоматически обрабатывает зависимости сервиса, создавая нужные экземпляры и внедряя их в компоненты или другие сервисы без ручного добавления в providers.
Использование providedIn уменьшает дублирование кода и снижает риск конфликтов при регистрации одного и того же сервиса в разных модулях. Локальная регистрация через модуль позволяет создавать отдельные экземпляры для разных частей приложения.
Различие между root и другими провайдерами

Параметр providedIn: ‘root’ в декораторе @Injectable создает один глобальный экземпляр сервиса, доступный во всех компонентах и модулях приложения. Такой сервис живет столько же, сколько и корневой инжектор, что экономит память и предотвращает дублирование объектов.
Локальные провайдеры, указанные через конкретный модуль или компонент, создают отдельные экземпляры сервиса для каждой области видимости. Например, если сервис зарегистрирован в модуле FeatureModule, Angular создаст отдельный экземпляр при загрузке этого модуля и будет использовать его только внутри модуля.
Различия проявляются в жизненном цикле и поведении состояния сервиса:
- Сервис с root сохраняет данные и состояние между разными модулями и компонентами.
- Локальный сервис повторно создается при каждом новом инстансе модуля или компонента с провайдером.
- Локальные провайдеры удобны для изоляции состояния между функциональными областями приложения.
Выбор между root и локальным провайдером зависит от того, требуется ли один общий экземпляр сервиса для всего приложения или отдельные независимые экземпляры для отдельных модулей или компонентов.
Внедрение сервиса в компонент через конструктор
В Angular сервисы внедряются в компоненты через конструктор. Это позволяет Angular автоматически создавать экземпляр сервиса и передавать его в компонент без ручного создания объектов.
Пример внедрения сервиса:
constructor(private myService: MyService) {}
После внедрения сервис доступен в компоненте через переменную класса, что позволяет использовать методы и свойства сервиса напрямую:
- Вызов методов сервиса для обработки данных.
- Подписка на Observable или Subject, реализованные в сервисе.
- Использование сервисного состояния для управления поведением компонента.
Рекомендации при внедрении через конструктор:
- Всегда указывайте модификатор доступа private или public, чтобы Angular смог корректно внедрить сервис.
- Избегайте ручного создания экземпляров через new, чтобы не нарушать единый жизненный цикл сервиса.
- Используйте интерфейсы или абстрактные классы при необходимости замены сервиса на mock-объекты в тестах.
Правильное внедрение через конструктор упрощает поддержку компонентов и позволяет централизованно управлять зависимостями, делая код более предсказуемым и тестируемым.
Использование нескольких экземпляров сервиса

В Angular можно создавать несколько экземпляров одного сервиса, регистрируя его на разных уровнях провайдеров. Это позволяет каждому модулю или компоненту иметь независимое состояние сервиса.
Пример создания отдельного экземпляра для модуля:
@NgModule({ providers: [FeatureService] })
export class FeatureModule {}
Пример создания отдельного экземпляра для компонента:
@Component({ selector: ‘app-sample’, providers: [LocalService] })
export class SampleComponent {}
Особенности использования нескольких экземпляров:
- Каждый локальный провайдер создает отдельный экземпляр сервиса, независимый от глобального или других локальных сервисов.
- Изоляция состояния полезна для функциональных блоков с уникальными данными, например, форм или временных списков.
- Следует учитывать потребление памяти, так как каждый экземпляр хранит свои данные и методы отдельно.
Рекомендации:
- Используйте глобальный сервис через providedIn: ‘root’ для общих данных и функций.
- Создавайте локальные экземпляры только при необходимости изоляции состояния или при lazy-загрузке модулей.
- Явно документируйте области действия сервисов, чтобы избежать путаницы между глобальными и локальными экземплярами.
Тестирование сервисов с @Injectable
Сервисы, помеченные декоратором @Injectable, легко тестировать благодаря Dependency Injection. Angular позволяет подставлять mock-объекты или spy-объекты вместо реальных сервисов без изменения кода компонента.
Пример теста с использованием TestBed:
beforeEach(() => {
TestBed.configureTestingModule({ providers: [MyService] });
service = TestBed.inject(MyService);
});
При тестировании можно:
- Подменять зависимости сервиса другими mock-сервисами через TestBed.overrideProvider.
- Следить за вызовами методов с помощью spyOn.
- Проверять состояние сервиса после выполнения методов или событий компонентов.
Рекомендации:
- Используйте отдельные экземпляры сервиса для каждого теста, чтобы исключить влияние предыдущих тестов.
- Тестируйте сервисы изолированно, не подключая лишние модули, если их функционал не нужен для конкретного теста.
- Включайте тесты для сервисов с глобальным состоянием через providedIn: ‘root’, чтобы контролировать его изменение при вызовах методов.
Тестирование сервисов с @Injectable делает код более надежным, позволяет быстро выявлять ошибки в логике бизнес-методов и облегчает поддержку приложения при масштабировании.
Ошибки при пропуске @Injectable и как их исправлять
Отсутствие декоратора @Injectable в сервисе Angular приводит к ошибкам внедрения зависимостей, чаще всего NullInjectorError. Это происходит, когда Angular пытается создать экземпляр класса через конструктор компонента или другого сервиса, но не находит метаданных о его зависимостях.
Основные причины ошибок и способы исправления можно систематизировать:
| Проблема | Причина | Решение |
|---|---|---|
| NullInjectorError при внедрении сервиса | Сервис не помечен @Injectable | Добавить @Injectable({ providedIn: ‘root’ }) или зарегистрировать сервис в providers модуля |
| Сервис создается несколько раз вместо одного экземпляра | Сервис зарегистрирован как провайдер на уровне компонента | Перенести регистрацию в корневой инжектор через providedIn: ‘root’ или в модуль для управления областью видимости |
| Ошибка при внедрении сервиса с зависимостями | Сервис зависит от других сервисов, но не имеет @Injectable | Добавить @Injectable и убедиться, что все зависимости тоже зарегистрированы |
Рекомендации:
- Всегда проверяйте наличие @Injectable для сервисов с зависимостями.
- Используйте providedIn для контроля жизненного цикла и области видимости сервиса.
- При возникновении NullInjectorError анализируйте стек вызовов и проверяйте регистрацию сервиса на всех уровнях.
Вопрос-ответ:
Почему Angular выдаёт NullInjectorError при попытке использовать сервис?
Ошибка NullInjectorError возникает, если сервис не помечен декоратором @Injectable или не зарегистрирован через providedIn или в массиве providers модуля. Angular не может определить, как создать экземпляр сервиса, поэтому внедрение зависимостей не выполняется. Чтобы исправить ошибку, необходимо добавить @Injectable({ providedIn: ‘root’ }) или явно зарегистрировать сервис в нужном модуле.
В чем разница между сервисом с providedIn: ‘root’ и сервисом, зарегистрированным в конкретном модуле?
Сервис с providedIn: ‘root’ создаёт один глобальный экземпляр на всё приложение, доступный во всех компонентах и модулях. Если сервис зарегистрирован в модуле, Angular создаёт отдельный экземпляр для этого модуля, который не используется в других модулях. Такой подход позволяет изолировать состояние сервисов для отдельных функциональных областей приложения.
Как внедрить сервис в компонент и использовать его методы?
Сервис внедряется через конструктор компонента, указывая его как приватное или публичное свойство. После этого методы и свойства сервиса становятся доступны через переменную класса. Например, constructor(private myService: MyService) { } позволяет вызывать методы myService.getData() или подписываться на Observables, предоставляемые сервисом.
Можно ли создать несколько экземпляров одного сервиса и для чего это нужно?
Да, это возможно, если сервис зарегистрирован как локальный провайдер на уровне модуля или компонента. В таких случаях Angular создаёт отдельные экземпляры сервиса для каждой области видимости. Это удобно, когда разные части приложения должны работать с независимым состоянием, например, для нескольких форм или временных списков данных, не влияя друг на друга.
Как тестировать сервисы с @Injectable и заменять их mock-объектами?
Для тестирования используется TestBed, который позволяет создавать экземпляры сервисов и заменять зависимости mock-объектами через TestBed.overrideProvider. Также можно применять spyOn для отслеживания вызовов методов. Такой подход позволяет проверять состояние сервиса после выполнения функций и контролировать влияние на компоненты без подключения всех реальных зависимостей.
Почему сервис в Angular не работает, если забыть про @Injectable?
Если сервис не имеет декоратора @Injectable, Angular не будет знать, что нужно использовать механизм Dependency Injection для его создания. В таком случае попытка внедрить сервис в компонент приведет к ошибке NullInjectorError, поскольку Angular не может автоматически создать экземпляр этого класса. Чтобы исправить эту ошибку, достаточно добавить @Injectable({ providedIn: ‘root’ }) к сервису или зарегистрировать его в providers соответствующего модуля.
Что такое providedIn в декораторе @Injectable и как это влияет на работу приложения?
Параметр providedIn в декораторе @Injectable указывает, где будет зарегистрирован сервис и в какой области он будет доступен. Если использовать providedIn: ‘root’, Angular создаст единственный экземпляр сервиса на уровне всего приложения, что полезно для сервисов с глобальным состоянием. Если указать имя модуля, например, providedIn: ‘SomeModule’, сервис будет доступен только в пределах этого модуля, и его экземпляры будут уникальными для каждого модуля. Это позволяет эффективно управлять областью видимости и жизненным циклом сервисов в Angular-приложении.
