angular - Vad är det bästa sättet att reagera på ngxs-statusändringar inom din komponent för laddnings- eller felhändelser?


Jag har en PostListComponent som itererar över alla inlägg som hämtats från servern och visar individuella PostItemComponent. Jag använder NGXS för tillståndshantering. När jag initierar fetchPosts-förfrågningar ändrar jag tillstånds-egenskapen loading: true. När förfrågan slutförs framgångsrikt ändrar jag den tillbaka till false. Om en förfrågan misslyckas uppdaterar jag felobjektet i tillståndet och uppdaterar felobjektets egenskap message och egenskapen exception.

Här är vad jag har hittills

`
export class PostListComponent implements OnInit {    
  private posts$: Observable<any> = this.store.select(state => state.posts);
  private isLoading: boolean = false;
  private error: object = {};

  constructor(
    public postsFacade: PostsFacadeService,
    public moviesFacade: MoviesFacadeService,
    private store: Store) {    
  }

  ngOnInit() {
    this.posts$.subscribe(
      (state) => this.onLoadingEvent(state)
      );

    this.posts$.subscribe(
      (error) => this.onErrorEvent(error)
    )
    // this.posts$.pipe(
    //   mergeMap(() => {
    //     // perform the subscription here and listen for state changes
    //   })
    // )

  }

  onErrorEvent(error)
  {
    this.error = error.error;
  }

  onLoadingEvent(state)
  {
    this.isLoading = state.loading;  
  }
`

Strukturen i min butik

`
@State<PostStateModel>({
    name: 'posts',
    defaults: {
        posts: [],
        loading: false,
        error: {
          message: "",
          error: ""                   
        },
    }
})
`

Jag känner att det finns ett bättre sätt att prenumerera på tillståndsförändringar i butiken genom att kanske använda rxjs mergeMap-operatorn. Jag är inte säker på om det är möjligt och om det nuvarande tillvägagångssättet är effektivt. Hur kan jag annars prenumerera på och reagera på butiksförändringar från min komponent?

Svar #1

Så några saker:

  1. Du behöver definiera några tillståndsåtgärder för din tillståndsklass - LoadPosts till exempel.
  2. Du bör skicka den åtgärden (säg i din ngOnInit), eller någonstans före komponenten laddas. Åtgärden bör anropa de tjänster som faktiskt hämtar datan, vilket därmed tar bort detta beroende från din komponent.
  3. Om tjänsten använder Observables eller Promises bör du hantera när de slutligen returnerar genom att uppdatera ditt tillstånd vid det tillfället.
  4. Du kan skapa din observable via @Select. Observera att det på detta sätt är typat.

    @Select(PostState) postState$: Observerbar;

  5. Du kan använda async-pipen i din komponent och du kanske inte behöver prenumerera på postState$ direkt i din kod (t.ex. för att använda *ngFor för att iterera genom inläggen).

  6. Eftersom ditt tillstånd inkluderar inläggen, laddningsflaggan och felet, kommer att prenumerera på hela tillståndet att göra att din prenumerationsfunktion eller asynkrona pipe körs oavsett vilken av dessa som ändras. Om du skapar selektormetoder med @Selector() i din tillståndsklass, kan du prenumerera på dessa för finare granularitet, till exempel för att bara reagera på laddningsflaggan.

Svar #2

Svaret verkar vara ganska tungt och inkluderar att sätta upp en interceptor för din applikation. Sedan kommer fel att bubbla upp till den.

ngxs.io/advanced/errors

Om du inte är redo att bygga den hela cykeln-uppsättningen kan du göra en lättviktig version genom att informera ett svarstatus "slice" resStatus.

export interface IReqStatus {
  loading?: boolean;
  error?: any;
  success?: boolean;
  action?: string; <-- optionally send along the action this belongs to
}

Exempel på åtgärd som gör en begäran till en API-slutpunkt

@Action(UpdateSomething)
updateSomething(ctx: StateContext<SomeStateModel>, { payload }: any): any {

   // 1. update the resStatus to show "busy"
  ctx.dispatch(new UpdateResStatus({ loading: true, action: 'updateSomething' }));

  return this.apiService.patch('/something/endpoint', payload).pipe(
    tap((res) => {
      // 2. update resStatus to show success
      ctx.dispatch(new UpdateResStatus(res));
    }),

    // 3. update the resStatus with errors
    catchError((err: any) => {
      return ctx.dispatch(new UpdateResStatus(err));
    })
  );
}

Använd en åtgärd för att hantera svarstatusen

@Action(UpdateResStatus)
updateResStatus(ctx: StateContext<SomeStateModel>, { payload }: any): Observable<any> {
  let status = {
    loading: false,
    success: payload?.status == 'success',
    error: payload?.status == 'success',
    ...(payload?.action && { action: payload?.action }),
  };
  ctx.setState(patch<SomeStateModel>({ reqStatus: status }));
  return of(payload);
}

I mitt fall svarar servern med en egenskap för framgångsstatus. Men om förfrågan är helt trasig bör catchError uppdatera reqStatus.

Du kan se statusen för dina förfrågningar med state.reqStatus var som helst. Och förhoppningsvis kan du till och med hålla dina async pipe's intakta som:

<button [loading]="$any(reqStatus$ | async).loading" ...>

Relaterade artiklar