Angular5中如何监听Ngrx动作/效果完成并通知组件?
Hey there! Let's break down your two questions one by one, since they're both about tracking NgRx action/effect completion in Angular 5.
问题1:Angular5环境下,如何在Ngrx动作/效果执行完成时收到通知?
In NgRx (matching Angular 5, you're likely using NgRx 4 or 5), there are a few practical ways to track when an action or effect finishes—pick the one that fits your use case:
Listen to the global action stream
You can directly subscribe tostore.actions$and filter for the "completion" actions your effects dispatch (usually a success or failure action after async work finishes). Here's an example:import { Store } from '@ngrx/store'; import { filter, takeUntil } from 'rxjs/operators'; import { Subject } from 'rxjs'; private destroy$ = new Subject<void>(); constructor(private store: Store) {} ngOnInit() { this.store.actions$.pipe( filter(action => action.type === '[Upload] Presign Success' || action.type === '[Upload] Presign Failure' ), takeUntil(this.destroy$) // Clean up subscription when component destroys ).subscribe(action => { if (action.type === '[Upload] Presign Success') { console.log('Upload presign action completed successfully!'); // Add your notification logic here—update UI, show a toast, etc. } else { console.error('Upload presign failed:', action.payload); } }); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); }Track via effect output actions
First, make sure your effect handles the trigger action, runs async logic, and dispatches a completion action. For example:import { Injectable } from '@angular/core'; import { Actions, Effect } from '@ngrx/effects'; import { of } from 'rxjs'; import { map, catchError, switchMap } from 'rxjs/operators'; @Injectable() export class UploadEffects { @Effect() uploadPresign$ = this.actions$.pipe( ofType(UploadActions.UploadPresignAction), switchMap(action => { // Your async logic here—e.g., call an API for presign token return this.uploadApi.getPresignToken(action.jwt, action.file) .pipe( map(res => new UploadActions.UploadPresignSuccessAction(res)), catchError(err => of(new UploadActions.UploadPresignFailureAction(err))) ); }) ); constructor(private actions$: Actions, private uploadApi: UploadApiService) {} }When the async work finishes, the effect will automatically dispatch a success/failure action—just listen for those in your component.
问题2:如何让组件收到UploadPresignAction分发完成的通知?
First, let's fix a subtle bug in your code: you're dispatching the action outside the userDetails$ subscription, but subscriptions are async. That means this.userDetails might not be set yet, leading to undefined for this.userDetails.jwt. Move the dispatch inside the subscription:
onUpload($event: SubmissionFileUpload) { this.homeService.getUserDetails().subscribe(res => { this.userDetails = res; // Dispatch only after we have the jwt this.store.dispatch(new UploadActions.UploadPresignAction(this.userDetails.jwt, $event)); }); }
Now, here are two solid ways to notify your component when the action completes:
方案1:Return an Observable from onUpload for the component to subscribe to
Modify your uploadService method to return an Observable that emits when the action finishes:
import { Store } from '@ngrx/store'; import { switchMap, filter, take } from 'rxjs/operators'; onUpload($event: SubmissionFileUpload) { return this.homeService.getUserDetails().pipe( switchMap(res => { this.userDetails = res; this.store.dispatch(new UploadActions.UploadPresignAction(res.jwt, $event)); // Listen for success/failure, take(1) ensures we only get one result return this.store.actions$.pipe( filter(action => action.type === '[Upload] Presign Success' || action.type === '[Upload] Presign Failure' ), take(1) ); }) ); }
Then in your component:
this.uploadService.onUpload({ some: data }).subscribe(action => { if (action.type === '[Upload] Presign Success') { // Action completed—run your logic alert('Upload presign finished successfully!'); } else { console.error('Upload failed:', action.payload); } });
方案2:Listen to store state changes (more aligned with NgRx principles)
If your reducer updates state when the action completes (e.g., adds an uploadStatus field), you can listen to that state in your component.
First, update your reducer to track status:
export interface UploadState { uploadStatus: 'idle' | 'loading' | 'completed' | 'failed'; // Other state fields... } const initialState: UploadState = { uploadStatus: 'idle' }; export function uploadReducer(state = initialState, action: UploadActions.UploadActions) { switch (action.type) { case UploadActions.UPLOAD_PRESIGN: return { ...state, uploadStatus: 'loading' }; case UploadActions.UPLOAD_PRESIGN_SUCCESS: return { ...state, uploadStatus: 'completed' }; case UploadActions.UPLOAD_PRESIGN_FAILURE: return { ...state, uploadStatus: 'failed' }; case UploadActions.RESET_UPLOAD_STATUS: return { ...state, uploadStatus: 'idle' }; default: return state; } }
Create a selector for the status:
import { createFeatureSelector, createSelector } from '@ngrx/store'; import { UploadState } from './upload.reducer'; export const selectUploadState = createFeatureSelector<UploadState>('upload'); export const selectUploadStatus = createSelector( selectUploadState, state => state.uploadStatus );
Then listen to it in your component:
import { Store } from '@ngrx/store'; import { selectUploadStatus } from './store/upload.selectors'; import { takeUntil } from 'rxjs/operators'; private destroy$ = new Subject<void>(); constructor(private store: Store) {} ngOnInit() { this.store.select(selectUploadStatus).pipe( takeUntil(this.destroy$) ).subscribe(status => { if (status === 'completed') { console.log('Upload presign completed!'); // Reset status to avoid duplicate notifications on next action this.store.dispatch(new UploadActions.ResetUploadStatusAction()); } else if (status === 'failed') { console.error('Upload presign failed'); } }); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); }
This approach uses state changes to drive UI updates, which fits NgRx's unidirectional data flow pattern.
内容的提问来源于stack exchange,提问作者Shamoon

