#angular #dependency-injection
#angular #внедрение зависимостей
Вопрос:
У меня есть универсальный класс, который использует http внутри него:
export abstract class GenericCrudService<T> {
constructor(protected http: HttpService, protected resourse: Resourse) {}
private entities: T[] = [];
entitiesChanged$: Subject<T[]> = new Subject();
getAllEntities() {
this.http.get<T>(environment.endpoint this.resourse).subscribe((res) => {
this.entities = res;
this.entitiesChanged$.next(this.entities);
});
}
createEntity(entity: T) {
return this.http
.post(environment.endpoint this.resourse, entity)
.subscribe((_) => {
this.entities.push(entity);
this.entitiesChanged$.next(this.entities);
});
}
deleteEntity(id: string) {
return this.http
.delete(environment.endpoint this.resourse, id)
.subscribe((res: any) => {
this.entities = this.entities.filter((e: any) => e._id !== res.deleted);
this.entitiesChanged$.next(this.entities);
});
}
updateEntity(id: string, newEntity: T) {
return this.http
.put(environment.endpoint this.resourse, id, newEntity)
.subscribe((res: any) => {
const updatedIndex = this.entities.findIndex(
(e: any) => e._id === res.updated
);
this.entities[updatedIndex] = newEntity;
this.entitiesChanged$.next(this.entities);
});
}
getLatestEntities() {
return this.entitiesChanged$.asObservable();
}
}
также есть эта служба, которая расширяет этот класс:
@Injectable({
providedIn: "root",
})
export class ExerciseService extends GenericCrudService<IExercise> {
constructor(http: HttpService) {
super(http, Resourse.exercise);
}
}
Я подумал, что было бы лучше не вводить http-клиент в службу упражнений, поскольку он ему не нужен, и создавать его только в общем, выполнив что-то вроде:
@Ввести (HTTP_TOKEN?) http: HTTPService и использовать this.http везде в службе. Мой вопрос в том, возможно ли это и рекомендуется ли это, и если да, то как я могу получить токен для внедрения HTTPService или как я могу его создать, если он не существует?
Комментарии:
1. JFYI:
resourse = Resourse.exercise;
строка избыточна, так как это уже сделано в parent . в вашей версии логика «назначения» просто дублируется.2. это было по ошибке
3. @Andrey итак, каково ваше предложение по рефакторингу в этом коде?
Ответ №1:
существует только один «элегантный» способ внедрить что-либо в Angular, и это способ конструктора. Другой способ — ввести инжектор в конструктор и Injector.get(токен) в любой момент времени выполнения.
myService = this.injector.get(MyService);
constructor(private injector: Injector) {}
в вашем коде это было бы
export abstract class GenericCrudService<T> {
protected http = this.injector.get(HttpService);
constructor(protected injector: Injector, protected resourse: Resourse) {}
...
}
....
export class ExerciseService extends GenericCrudService<IExercise> {
constructor(injector: Injector) {
super(injector, Resourse.exercise);
}
}
тогда в любых потомках вы передадите инжектор только родительскому, и parent может вводить все, что захочет, с помощью этого инжектора
Комментарии:
1. так что лучше делать это так, как я, чем @Inject(HTTP_TOKEN?) http: HTTPService в службе? Что, если я расширил более двух уровней? Как вы думаете, правильно ли передавать зависимости таким образом?
2. вы могли бы использовать angular DI, если обсуждение касалось компонентов, потому что у них есть инжекторы уровня компонентов. это было бы возможно
@Component({... providers: [provide: Resource, useValue: Resource.excercise]}) class MyClass {}
; но жаль, что службы не могут этого сделать. Если у вас есть планы многоуровневого наследования, то я бы использовал Injector в конструкторе, а затем внедрил все, что нужно, в parent. Это лучше, если вы хотите добавить зависимости на одном из «родительских» уровней. никаких других обновлений, кроме 1 класса, не потребуется.3. Не могли бы вы показать мне, как это реализовано в моем коде, пожалуйста?
4. Спасибо за вашу помощь 👍