我正在角度服务层上创建商店。
private attributes: Observable<Attribute[]>;
private attributes$: BehaviorSubject<Attribute[]>;
当用户要求allAttributes()
时填充的。然后,随后对所有属性或单个属性(getAttribute(id)
)的请求都从同一存储返回数据。
这是我的getAttribute()
getAttribute(id: number): Promise<Observable<Attribute>> {
return new Promise((resolve) => {
let _attributeObservable;
const _attributes: Attribute[] = this.getAttributesState();
let _attributeFound = false;
for (const _attribute of _attributes) {
if (_attribute.id === id) {
_attributeFound = true;
break;
}
}
if (_attributeFound) {
_attributeObservable = this.attributes.pipe(map((_attributeList: Attribute[]) => {
return _attributeList.find(_attribute => _attribute.id === id);
}));
resolve(_attributeObservable);
} else {
return this.http.get(`${this.context}/attributeService/getAttribute/${id}`)
.subscribe((_attributeInfo: Attribute) => {
const _allAttributes = this.getAttributesState();
_allAttributes.push(_attributeInfo);
// push object to store that was not found
this.attributes$.next(Object.assign([], _allAttributes));
_attributeObservable = this.attributes.pipe(map((_attributeList: Attribute[]) => {
return _attributeList.find(_attribute => _attribute.id === id);
}));
resolve(_attributeObservable);
});
}
});
}
和
getAttributesState(): Attribute[] {
return this.attributes$.getValue();
}
现在有些情况下,某些其他用户可以添加属性,因此该属性将不在商店中。因此,如果找不到请求的属性,则会发出http请求并将其保存到存储中。
但是问题是,如果找到属性,则它可以工作,但else部分不起作用。可能是什么问题?可以简化此代码,更好的方法吗?
答案 0 :(得分:0)
经过一段时间的代码重构,我想我了解该代码的用途。
据我正确理解 如果已经存储了属性,则要避免服务器调用。通过在BehaviourSubject中查找给定ID的属性,指示存储的属性。 如果找不到属性,则代码将触发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,我们无法确定服务器何时响应。
IMO,您可以做的是在组件上引入一个新属性。此属性包含一个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
等来获得第一个可行的解决方案。