В Angular сервисы играют важную роль, предоставляя общую логику для компонентов, директив и других сервисов. Они позволяют инкапсулировать функциональность и обеспечивать её повторное использование, а также делиться состоянием или данными между различными частями приложения. Для того чтобы сервисы были доступны для внедрения через механизм зависимости (dependency injection), их нужно правильно зарегистрировать.
В Angular существуют несколько способов регистрации сервисов, каждый из которых имеет свои особенности. Рассмотрим основные из них.
providedIn в декораторе @InjectableСамый современный и рекомендованный способ регистрации сервисов — это
использование свойства providedIn в декораторе
@Injectable. Этот способ позволяет указать, где именно
будет доступен сервис, и устраняет необходимость явного добавления
сервисов в массив providers внутри модулей или
компонентов.
Пример регистрации сервиса с использованием
providedIn:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // сервис доступен в корневом инжекторе
})
export class MyService {
constructor() { }
getData() {
return 'Some data';
}
}
Здесь ключевое слово providedIn: 'root' указывает, что
сервис будет доступен на уровне корневого инжектора, и Angular
автоматически позаботится о его регистрации в нужных местах.
Примечание: Если указано значение
'root', сервис будет доступен в приложении по умолчанию.
Можно также указать другой уровень видимости, например,
'any', что означает доступность сервиса на уровне
инжектора, создаваемого для каждого компонента, или module
для регистрации в определённом модуле.
Если сервис необходимо зарегистрировать только в рамках конкретного
модуля, его добавляют в массив providers этого модуля. В
этом случае сервис будет доступен только тем компонентам, которые
принадлежат данному модулю или его дочерним модулям.
Пример:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MyService } from './my-service.service';
@NgModule({
imports: [CommonModule],
providers: [MyService] // регистрация в модуле
})
export class FeatureModule { }
В этом примере сервис будет доступен только в компонентах, которые
находятся в модуле FeatureModule или в дочерних модулях,
если они явно его импортируют.
В случае, если сервис нужен только в одном компоненте, его можно зарегистрировать непосредственно в этом компоненте. Это удобно, если сервис используется только внутри одного компонента и не должен быть доступен в других частях приложения.
Пример регистрации в компоненте:
import { Component } from '@angular/core';
import { MyService } from './my-service.service';
@Component({
selector: 'app-my-component',
templateUrl: './my-component.component.html',
providers: [MyService] // регистрация только для этого компонента
})
export class MyComponent {
constructor(private myService: MyService) { }
}
В этом случае сервис будет доступен только в контексте компонента
MyComponent и его дочерних компонентов.
Angular предоставляет несколько уровней видимости для сервисов, которые контролируют, где и как будет доступен сервис.
Корневой инжектор (root)
Использование providedIn: 'root' гарантирует, что сервис
будет доступен в рамках всего приложения. Этот подход является наиболее
удобным, поскольку он устраняет необходимость явно регистрировать сервис
в модулях или компонентах. Сервис, зарегистрированный в корневом
инжекторе, будет иметь одну инстанцию на всё приложение.
Модульный инжектор
Сервис, добавленный в массив providers модуля, будет
доступен только в компонентах, принадлежащих этому модулю или его
дочерним модулям. Такой сервис будет иметь одну инстанцию для всего
модуля, и каждый компонент, использующий этот сервис, получит доступ к
одной и той же инстанции.
Компонентный инжектор
Когда сервис регистрируется в компоненте, он доступен только этому компоненту и его дочерним компонентам. Каждый компонент получает свою собственную инстанцию сервиса, что может быть полезно, если сервис содержит состояние, специфичное для компонента.
После того как сервис зарегистрирован, его можно использовать в компонентах, директивах, других сервисах и даже в других Angular-хармониках через механизм Dependency Injection (DI). Для внедрения зависимостей достаточно указать сервис в конструкторе нужного класса.
Пример использования сервисов в компоненте:
import { Component } from '@angular/core';
import { MyService } from './my-service.service';
@Component({
selector: 'app-my-component',
templateUrl: './my-component.component.html'
})
export class MyComponent {
constructor(private myService: MyService) { }
ngOnInit() {
console.log(this.myService.getData());
}
}
В этом примере Angular автоматически создаст экземпляр
MyService и внедрит его в конструктор компонента
MyComponent. Внутри компонента сервис можно использовать
как обычный объект.
Жизненный цикл сервисов напрямую связан с областью видимости, в которой они были зарегистрированы. Сервисы, зарегистрированные в корневом инжекторе, существуют на протяжении всего времени жизни приложения. Если сервис зарегистрирован в модуле или компоненте, он будет создан заново каждый раз, когда Angular создает соответствующий инжектор. Это может быть полезно для оптимизации использования ресурсов, особенно когда сервисы содержат состояние, которое нужно сбрасывать или обновлять при каждом новом инстанцировании.
Когда сервис зарегистрирован в корневом инжекторе, он, как правило, становится синглтоном, то есть существует только один его экземпляр на всё приложение. Это поведение обеспечивается тем, что Angular создает одну инстанцию и делится ею между всеми компонентами, которые его используют. Если сервис зарегистрирован на уровне модуля или компонента, то для каждого инжектора будет создан новый экземпляр этого сервиса.
Для более сложных сценариев можно использовать комбинацию нескольких методов регистрации сервисов. Например, можно зарегистрировать сервис на уровне модуля, а в его методах использовать другие сервисы, которые могут быть зарегистрированы на уровне компонентов.
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MyService } from './my-service.service';
import { AnotherService } from './another-service.service';
@NgModule({
imports: [CommonModule],
providers: [MyService, AnotherService]
})
export class FeatureModule { }
В таком случае каждый компонент, который использует
FeatureModule, получит доступ к MyService и
AnotherService.
Регистрация сервисов в Angular — это важный аспект работы с этим
фреймворком. Правильный выбор метода регистрации помогает обеспечить
оптимальную производительность и простоту масштабирования приложения.
Использование декоратора @Injectable с параметром
providedIn позволяет упростить управление сервисами и
сделать их доступными без необходимости вручную добавлять их в
providers.