[英]How do I make a component wait for async data from service
A service is fetching api data and I need to keep and use it in many other components every time the url changes (router links) without calling api again.一项服务正在获取 api 数据,每次 url 更改(路由器链接)时,我都需要在许多其他组件中保留和使用它,而无需再次调用 api。 Api should fetch only when the service runs.
Api 应该仅在服务运行时获取。
I need to make the component method wait until data is available.我需要让组件方法等到数据可用。
(Real code reduced to minimum for readability) (为了便于阅读,实际代码减少到最低限度)
constructor(private http: HttpClient) {
this.fetchData();
}
fetchData(): void {
this.http.get(this.endpointUrl).subscribe((res)=>{
this.data = res
});
}
getData(selectedCategory): DataType {
return this.data.find(cat => cat === selectedCategory)
}
ngOnInit(): void {
this.activatedRoute.paramMap.subscribe(
(params: ParamMap) => {
// I need params before calling service method
// this method must wait until the "data" is available in service.
this.selectedCategory = this.service.getData(params.category);
}
);
}
Without many changes, it would be possible to subscribe to fetchData()
which instead returns an observable transporting requested data.如果没有很多更改,就可以订阅
fetchData()
,它会返回一个可观察的传输请求的数据。 We use tap
for the side effect of also setting this.data
in the Service.我们使用
tap
来获得在服务中设置this.data
的副作用。 Don't forget to unsubscribe()
, Also mind some syntax errors.不要忘记
unsubscribe()
,还要注意一些语法错误。 I didn't check for them Suggesting to add typings for <any>
.我没有检查他们建议为
<any>
添加类型。
fetchData(): Observable<any>{
return this.http.get(this.endpointUrl).pipe(
tap(res => this.data = res),
catchError(console.log(err))
)
}
ngOnInit(): void {
this.activatedRoute.paramMap.subscribe(
(params: ParamMap) => {
this.myService.fetchData().subscribe(res => {
this.selectedCategory = this.service.getData(params.category);
// Or use
// this.selectedCategory = this.res.find(cat => cat === params.category)
});
}
);
}
Though subscribing in service is not advised (especially onRoot
Service) because the service will do a request every time url changes, but I guess this is what you want.尽管不建议订阅服务(尤其是
onRoot
服务),因为该服务每次url 更改时都会发出请求,但我想这就是您想要的。 So be warned.所以要警告。
We subscribe to param changes in constructor.我们订阅构造函数中的参数更改。 Service will request new data on each route change.
服务将在每次路线更改时请求新数据。 Queried data will be emitted using
BehaviourSubject
which retains and updates data.查询的数据将使用保留和更新数据的
BehaviourSubject
发出。 Components can subscribe to BehaviourSubject
instead using | async
组件可以订阅
BehaviourSubject
,而不是使用| async
| async
pipe or manual subscription. | async
pipe 或手动订阅。
Service:服务:
let data = new BehaviorSubject(null);
constructor() {
this.activatedRoute.paramMap.subscribe(
(params: ParamMap) => {
this.fetchData().subscribe(res => {
this.data.next(res);
});
}
);
}
I would suggest using the async pipe (if you need to display the data) and you should avoid nesting subscriptions.我建议使用异步 pipe (如果您需要显示数据)并且您应该避免嵌套订阅。
For data persistence over page refresh, you'll need to use the local storage or the session storage.对于页面刷新后的数据持久性,您需要使用本地存储或 session 存储。 I'm not a big fan of this, but I guess it depends on your requirements.
我不是这个的忠实粉丝,但我想这取决于你的要求。
You can manage your state with the help of a BehaviourSubject, but if you want to do more complicated stuff and to keep your code clean, I'll recommend using a state management library like @ngrx/component-store .您可以在 BehaviourSubject 的帮助下管理您的 state,但如果您想做更复杂的事情并保持代码整洁,我建议您使用 state 管理库,如@ngrx/component-store 。
Your service您的服务
private readonly _state = newBehaviourSubject<Record<string, DataType>>(JSON.parse(localStore.getItem('state')) ?? {});
readonly state$ = this._state.asObservable();
getData(category: string) {
return of(category).pipe(
withLatestFrom(this.state$), // you don't want to do something like this.state.pipe(...) because you'll resubscribe to it multiple times because the implementation below
switchMap(([key, data]) => {
if (key in data) return of(data[key]); // return the found category
return this.http.get(this.endpointUrl).pipe(
tap(res => {
const updatedData = { ...data, [key]: res };
this._state.next(updatedData); // update your state
localStorage.setItem('state', JSON.stringify(updatedData) // update local storage
})
);
}
)
}
Component零件
readonly selectedCategory$ = this.activatedRoute.paramMap.pipe(
map(paramMap => paramMap.get('category')), // I assume that you expect a string
filter(category => !!category) // it makes sure that the result is not null or undefined
switchMap(category => this.service.getData(category)),
catchError(error => {...}), // handle your errors
filter(res => !!res) // if you want to make sure the response is not null
)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.