Инъекция зависимостей (DI) — это один из ключевых паттернов проектирования в Angular, который помогает управлять зависимостями компонентов, сервисов и других объектов приложения. В Angular инъекция зависимостей используется для того, чтобы автоматически предоставлять нужные объекты в компоненты, директивы и сервисы без необходимости их явного создания.
Инъекция зависимостей предполагает, что объекты (например, сервисы) не создаются вручную внутри компонентов, а передаются извне. Angular использует специальный механизм для автоматического создания и предоставления зависимостей в нужные места, что снижает связанность компонентов и упрощает тестирование, поддержку и расширяемость приложения.
Каждая зависимость в Angular реализуется как сервис, который может быть внедрён в компоненты или другие сервисы с помощью механизма DI. Angular предоставляет контейнер, называемый инжектор, который управляет созданием и передачей зависимостей.
@Injectable, который сообщает
Angular, что класс может быть инъецирован как зависимость в другие
компоненты или сервисы.Чтобы сервис можно было внедрить в другие части приложения, его нужно зарегистрировать в поставщике (provider). Поставщики могут быть привязаны к различным уровням: компонентам, модулям или всему приложению. В зависимости от области видимости и времени жизни сервиса, Angular использует различные стратегии для его создания и предоставления.
Пример регистрации сервиса на уровне модуля:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // Сервис доступен во всём приложении
})
export class DataService {
getData() {
return 'Data from service';
}
}
Если указать providedIn: 'root', сервис будет доступен
для всего приложения. Такой подход упрощает конфигурацию, так как
Angular сам создаёт и управляет временем жизни этого сервиса.
После регистрации сервисов их можно внедрять в компоненты и другие сервисы с помощью конструктора. Angular автоматически определяет, какие зависимости нужно передать, и инжектирует их в соответствующие компоненты.
Пример инъекции сервиса в компонент:
import { Component } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-my-component',
template: `<h1>{{ data }}</h1>`
})
export class MyComponent {
data: string;
constructor(private dataService: DataService) {
this.data = this.dataService.getData();
}
}
В данном примере сервис DataService инъецируется в
компонент через его конструктор. Angular автоматически передаст
экземпляр DataService в компонент, и тот может использовать
его методы и данные.
Angular поддерживает различные типы инъекции зависимостей, что даёт гибкость в управлении временем жизни объектов и областью их видимости:
Angular управляет временем жизни зависимостей через область видимости и область действия инжектора:
providedIn: 'root'
сервис будет создан один раз для всего приложения. Это означает, что для
всех компонентов и сервисов, использующих этот сервис, будет передан
один и тот же экземпляр.Инъекция зависимостей в Angular по умолчанию выполняется при создании
компонента или сервиса, то есть жадно. Однако в
некоторых случаях может потребоваться ленивая
(отложенная) инъекция, когда зависимость будет создана только тогда,
когда она действительно понадобится. Для этого можно использовать
Injector и Injectable с дополнительной
настройкой.
Пример ленивой инъекции:
import { Injector, Inject } from '@angular/core';
export class LazyComponent {
constructor(private injector: Injector) {}
loadService() {
const service = this.injector.get(DataService);
console.log(service.getData());
}
}
Инъекция зависимостей значительно упрощает тестирование Angular-приложений. С помощью DI можно легко заменить реальные зависимости на mock-объекты или фейки, что позволяет изолировать тестируемый компонент или сервис от внешних зависимостей.
Пример теста компонента с подменой зависимости:
import { TestBed } from '@angular/core/testing';
import { MyComponent } from './my.component';
import { DataService } from './data.service';
class MockDataService {
getData() {
return 'Mock data';
}
}
describe('MyComponent', () => {
let component: MyComponent;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ MyComponent ],
providers: [{ provide: DataService, useClass: MockDataService }]
});
component = TestBed.createComponent(MyComponent).componentInstance;
});
it('should display mock data', () => {
component.ngOnInit();
expect(component.data).toBe('Mock data');
});
});
В данном примере с помощью TestBed подменяется реальный
сервис на его мок-версию, что позволяет протестировать компонент в
условиях, близких к реальному использованию, без зависимости от внешних
API.
Инъекция зависимостей является важнейшей частью архитектуры Angular-приложений. Она обеспечивает гибкость, облегчает поддержку и тестирование приложений. Понимание механизма DI позволяет создавать масштабируемые и легко поддерживаемые приложения, где компоненты и сервисы остаются слабо связанными и легко заменяемыми.