“ @ angular / core”:“ ^ 5.2.0”
“ rxjs”:“ ^ 5.5.6”
我对angular框架不熟悉,目前正在使用它来创建可注入服务类,该类将进行WebAPI调用以从后端数据库中提取数据。
我想保留或避免来自使用此服务类的组件的后续调用。因为它的相同数据一次又一次被调用。
我觉得我犯了一个很小的错误,无法抓住它。我想避免第二次通话的往返。 在第一次调用期间,我想将数据保留在_dyndata对象中。但是以某种方式,对于每个服务调用,它都在打印console.log('Fresh call')语句。
请提出我在哪里犯错。我想使用角度变化检测的内置功能。
我的服务类别如下:-
@Injectable()
export class DynamicDataService {
private _dyndata: DynamicContent[];
constructor(private _httpObj: HttpClient, private http: Http) {
}
headers = new HttpHeaders({
'Content-Type': 'application/json'
});
getdynamiccontent(): Observable<DynamicContent[]> {
if (this._dyndata) {
console.log('data exists!')
return of(this._dyndata);
}
console.log('Fresh call');
return this._httpObj.get<DynamicContent[]>(environment.apiHost + 'DynamiContentforPage')
.pipe(tap(data => this._dyndata = data)
);
}
使用该服务类的组件如下所示:-
import { Component, ElementRef, Input, Renderer, OnInit, Inject, DoCheck } from '@angular/core';
import { DynamicDataService } from './dynamicdata.service';
import 'rxjs/add/operator/filter';
import { DynamicContent } from '../interfaces/IDynamicContent';
@Component({
selector: 'CMSBlock',
template: `<div [innerHTML]="_dynmarkup | keepHtml"></div>`,
providers: [DynamicDataService]
})
export class DynamicDataComponent {
public markupcollection: DynamicContent[];
public savedmarkup: DynamicContent[];
@Input('key') key: string;
public _dynmarkup: any = '';
constructor(private dynamicdataService: DynamicDataService) {
console.log('cstor :: dynamicdata comp');
this.getCMSData();
}
public getCMSData() {
this.dynamicdataService.getdynamiccontent()
.subscribe(px => {
this.setUsersArray(px);
console.log(this.markupcollection);
});
}
setUsersArray(data) {
this.savedmarkup = data;
if (this.savedmarkup != undefined) {
var item = this.savedmarkup.find(fx => fx.Key == this.key);
this._dynmarkup = item.Text
}
}
}
答案 0 :(得分:1)
您遇到了一个非常有趣的案例。
解决问题的简短答案,就是从组件提供商中删除 DynamicDataService
,并在更合适的位置包含,例如root
。
因此,现在让我们深入探讨问题,当按角度使用服务时,我们将它们包括在我们要在其中使用它们的模块/组件(在您的情况下为CMSBlock
)的提供者中,这样一来,您就可以使用该服务,并且可以调用API
并加载所需的数据。但是据我们所知,服务就是这样,一旦提供就可以访问大厅应用,因此人们可以问自己一个问题,当我们提供具有相同令牌(名称)的多个服务,或相同服务多次(与提供具有相同名称的不同服务具有相似的效果)。答案是,最后一个获胜,或者换句话说,最后提供的服务将覆盖该服务提供者的功能{{ 1}}。
在您使用多次 token
组件的情况下,您要提供/初始化{strong>两次 CMSBlock
,这意味着每次调用服务时,DynamicDataService
处于初始状态,在您的情况下为空,这要归功于“新呼叫”。
解决此问题的最佳方法是在关闭父模块(如果您在延迟加载的模块中使用该服务)或 _dyndata
中提供该服务模块(如果您正在使用它急切加载)。
编辑:
第二个调用也可能在第一个调用完成之前发生,因此,对可用root
的检查将失败。
这是一个现场演示,介绍了我所谈论的内容:CodeSandbox
答案 1 :(得分:1)
从对该问题的评论看来,您正在实例化该组件的两个实例,并且它们都在完成一个请求之前就开始了请求。
因此,要解决此问题,您需要重组服务如何处理请求。我建议在服务上使用BehaviorSubject来提供详细信息,如下所示:
@Injectable()
export class DynamicDataService {
private _dyndata: BehaviorSubject<DynamicContent[]> = new BehaviourSubject<DynamicContent[]>(null);
private _requested: boolean = false;
constructor(private _httpObj: HttpClient, private http: Http) {
}
headers = new HttpHeaders({
'Content-Type': 'application/json'
});
getdynamiccontent(): Observable<DynamicContent[]> {
if (!this._requested) {
this._requested = true;
console.log('Fresh call');
this._httpObj.get<DynamicContent[]>(environment.apiHost + 'DynamiContentforPage')
.subscribe(data => this._dyndata.next(data));
} else {
console.log('Request already made');
}
return this._dyndata.asObservable().pipe(filter((e) => e != null), take(1));
}
}
答案 2 :(得分:1)
您看到的问题是,在很短的时间内,您可能有多个组件调用this.dynamicdataService.getdynamiccontent()
,同时又创建了多个HTTP请求,然后它们中的任何一个都完成,并将任何值设置为this._dyndata
。
使用RxJS的最佳解决方案是使用publishReplay(1)
和take(1)
运算符:
private cachedRequest = this._httpObj.get(...).pipe(
shareReplay(1),
take(1),
)
getdynamiccontent() {
return this.cachedRequest;
}
您甚至不需要使用任何中间变量来存储数据。 shareReplay
运算符始终仅保留对this._httpObj.get(...)
的一个订阅,而take(1)
确保在响应准备就绪时正确完成链接。多亏了take(1)
缓存在shareReplay(1)
中的IsGrouped
,任何后续的订阅者都将只获得一个值。
实时演示:https://stackblitz.com/edit/rxjs6-demo-zanygw?file=index.ts