简体   繁体   English

如何让组件等待来自服务的异步数据

[英]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) (为了便于阅读,实际代码减少到最低限度)

Service服务

  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)
  }

Component零件

  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)
         });  
      }
    );
  }

Subscribe in service订阅服务

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.

相关问题 在父组件中,我提出一个服务请求以获取数据。 如何告诉我的孩子组件等待响应? - In my parent component, I make a service request to fetch data. How do I tell my child component to wait for a response? 如何从异步存储中检索数据并在我的组件中使用它? - How do i retrieve data from Async storage and use it in my component? React-Redux:如何使用异步调用中的数据填充加载时的组件的prop? - React-Redux: How do I populate a component's prop on load with data from an async call? 如何让 ASync 函数等待,直到收集到所有信息? - How do I make an ASync function wait, till all the information is gathered? 如何在Angular 5中使组件侦听并等待来自另一个组件的数据而没有事件? - How to make a component listen and wait for data from another component without an event in Angular 5? 如何使异步等待下一个? - How to make async wait for the next? Javascript:如何等到异步响应需要数据后再继续 - Javascript: How do I wait until async response has required data before continuing 如何在组件方法中等待来自父级的数据 - How to wait for data from a parent in a component method 如何同时调用异步函数并等待所有回调? - How do I simultaneously call async functions and wait for all callbacks? 如何等待进行异步调用的.each循环? - How do I wait for a .each loop that makes async calls?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM