После некоторого времени рефакторинга кода мне кажется, что я понимаю, для чего предназначен этот код.
Насколько я понимаю, вы хотите избежать обращения к серверу, если атрибут уже сохранен.При поиске атрибута для данного идентификатора в вашем BehaviourSubject укажите сохраненный атрибут.Если вы не нашли атрибута, код будет запускать http-клиент для получения атрибута с сервера.
Очистка выглядит следующим образом.
import { Component, OnInit } from '@angular/core';
import { Observable, BehaviorSubject, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { Attribute } from '../attribute';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-attribute',
templateUrl: './attribute.component.html',
styleUrls: ['./attribute.component.scss']
})
export class AttributeComponent implements OnInit {
private attributesAsObservable: Observable<Attribute[]>;
private attributes$: BehaviorSubject<Attribute[]>;
private context = 'localhost:3000';
constructor(private http: HttpClient) { }
ngOnInit() {
let attributes = [{id: 12, name: 'test'}] as Attribute[];
this.attributes$ = new BehaviorSubject<Attribute[]>(attributes)
this.attributesAsObservable = of(attributes)
console.log("Find id: 12", this.getAttribute(12))
console.log("Find id causes server call: 1", this.getAttribute(1))
}
getAttribute(id: number): Observable<Attribute> {
let attributeFound = this.findFromStored(id);
if (attributeFound) {
return of(attributeFound)
} else {
return of(this.fetchAttributeFromServer(id))
}
}
private findFromStored(id: number): Attribute {
let attributes = this.attributes$.getValue();
return attributes.find(attribute => attribute.id === id)
}
private fetchAttributeFromServer(id: number): Attribute {
this.httpCall(id).subscribe( attribute => {
this.addNewAttributeToStore(attribute);
});
}
private addNewAttributeToStore(attribute: Attribute) {
let attributes: Attribute[] = this.attributes$.getValue();
attributes.push(attribute)
this.attributes$.next(attributes)
}
//THIS SHOULD BE EXTRACTED TO A SERVICE
private httpCall(id: number): Observable<Attribute> {
console.log('Return fake http Observable');
return of<Attribute>({id: 1, name: 'test'})
// return this.http.get<Attribute>(
// `${this.context}/attributeService/getAttribute/${id}`
// );
}
}
Этот рефакторинг не работает, если вы получаете значение с сервера.Причина - асинхронный http-вызов.HTTP-клиент вернется в Observable, и мы не можем быть уверены, когда сервер ответит.
ИМО, что вы можете сделать, это ввести новое свойство для вашего компонента.Это свойство содержит BehaviourSubject<Attribute>
(или в вашем случае BehaviourSubject<Observable<Attribute>>
).Давайте назовем это currentAttribute $.Каждый раз, когда вы звоните getAttribute(id)
, вы собираетесь звонить currentAttribute$.next()
.
Позволяет изменить его.
import { Component, OnInit } from '@angular/core';
import { Observable, BehaviorSubject, of } from 'rxjs';
import { Attribute } from '../attribute';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-attribute',
templateUrl: './attribute.component.html',
styleUrls: ['./attribute.component.scss']
})
export class AttributeComponent implements OnInit {
private attributesAsObservable: Observable<Attribute[]>;
private attributes$: BehaviorSubject<Attribute[]>;
private currentAttributeFoundById: BehaviorSubject<Attribute>;
private context = 'localhost:3000';
constructor(private http: HttpClient) { }
ngOnInit() {
let attributes = [{id: 12, name: 'test'}] as Attribute[];
this.attributes$ = new BehaviorSubject<Attribute[]>(attributes);
this.attributesAsObservable = of(attributes);
this.currentAttributeFoundById = new BehaviorSubject<Attribute>({});
this.currentAttributeFoundById.subscribe(attribute => {
console.log('Current Attribute by ID is:', attribute)
});
this.setAttributeBy(12);
this.setAttributeBy(12);
this.setAttributeBy(1);
}
setAttributeBy(id: number) {
let attributeFound = this.findFromStored(id);
if (attributeFound) {
this.currentAttributeFoundById.next(attributeFound);
} else {
this.setAttributeFromServer(id)
}
}
private findFromStored(id: number): Attribute {
let attributes = this.attributes$.getValue();
return attributes.find(attribute => attribute.id === id)
}
private setAttributeFromServer(id: number) {
this.httpCall(id).subscribe(attribute => {
this.addNewAttributeToStore(attribute);
this.currentAttributeFoundById.next(attribute);
});
}
private addNewAttributeToStore(attribute: Attribute) {
let attributes: Attribute[] = this.attributes$.getValue();
attributes.push(attribute)
this.attributes$.next(attributes)
}
//THIS SHOULD BE EXTRACTED TO A SERVICE
private httpCall(id: number): Observable<Attribute> {
console.log('Return fake http Observable');
return of<Attribute>({id: 1, name: 'test'})
// return this.http.get<Attribute>(
// `${this.context}/attributeService/getAttribute/${id}`
// );
}
}
Это изменение позволяет коду вести себя так, как задумано (только при необходимости извлекать данные с сервера).
Как указано в комментариях, вы можете использовать switchMap
, concatMap
,mergeMap
и т. Д., Чтобы получить первое решение для работы.