ngrx-for循环-当前元素的处理完成后获取下一个元素

时间:2019-05-14 05:30:26

标签: typescript angular6 ngrx

抱歉,仍然有任何疑问。我有一个要求用户必须完成6个步骤才能完成1个活动的要求。如果用户单击步骤6的URL,那么我首先必须检查他们是否已完成前面的5个步骤。如果没有,那么我必须指出用户已经完成了哪些步骤,哪些尚未完成。对于待处理的邮件,我还要添加URL,以便用户可以轻松地重定向。

问题是,我正在遍历每个步骤,但是由于NG RX,它没有等待Action完成并转到下一步。我无法确定以前的步骤是否真的完成了。

我正在使用NG RX动作,减速器和效果技术。无论如何,我可以等待循环完成第一个动作。一旦完成第一项操作,然后从数组中获取第二个元素。

// This is part of code in effect file.    
const steps = [Step.Step1, Step.Step2, Step.Step3]

    // It should take next element once evaluation of step1 is completed.
    steps.every((currentStep: Step) => {
                        currentDependentStep = currentStep;
                        switch (currentStep) {
                            case Step.Step1:
                                this.store.dispatch(new fromSomeAction.SomeAction1);
                                this.store.dispatch(new fromSomeAction.SomeAction2);

                                this.store.combineLatest(
                                    this.store.select(fromApp.getData1),
                                    this.store.select(fromApp.getData2)
                                ).first(([payload, data1, data2]) => !!data1 && !!data2)
                                    .subscribe(([payload, data1, data2]) => {
                                        // It should not take second element till I get below result whethere it is completed or not.
                                        isCompleted = <check for some condition>;
                                    });
                                break;
                            case Step.Step2:
                                this.store.dispatch(new fromSomeAction.SomeAction1);
                                this.store.dispatch(new fromSomeAction.SomeAction2);

                                this.store.combineLatest(
                                    this.store.select(fromApp.getData1),
                                    this.store.select(fromApp.getData2)
                                ).first(([payload, data1, data2]) => !!data1 && !!data2)
                                    .subscribe(([payload, data1, data2]) => {
                                        isCompleted = <check for some condition>;
                                    });
                                break;
                            case Step.Step3:
                                this.store.dispatch(new fromSomeAction.SomeAction1);
                                this.store.dispatch(new fromSomeAction.SomeAction2);

                                this.store.combineLatest(
                                    this.store.select(fromApp.getData1),
                                    this.store.select(fromApp.getData2)
                                ).first(([payload, data1, data2]) => !!data1 && !!data2)
                                    .subscribe(([payload, data1, data2]) => {
                                        isCompleted = <check for some condition>;
                                    });
                                break;
                                break;
                            default:
                                isCompleted = false;
                                break;
                        }

                        return !isCompleted;
                    });

                    // Call completed action with payload based on isCompleted

注意:抱歉,我没有实际的代码可在此处分享。

1 个答案:

答案 0 :(得分:1)

我认为最好的方法是通过路由器防护,请参阅example-app作为示例。

@Injectable({
  providedIn: 'root',
})
export class BookExistsGuard implements CanActivate {
  constructor(
    private store: Store<fromBooks.State>,
    private googleBooks: GoogleBooksService,
    private router: Router
  ) {}

  /**
   * This method creates an observable that waits for the `loaded` property
   * of the collection state to turn `true`, emitting one time once loading
   * has finished.
   */
  waitForCollectionToLoad(): Observable<boolean> {
    return this.store.pipe(
      select(fromBooks.getCollectionLoaded),
      filter(loaded => loaded),
      take(1)
    );
  }

  /**
   * This method checks if a book with the given ID is already registered
   * in the Store
   */
  hasBookInStore(id: string): Observable<boolean> {
    return this.store.pipe(
      select(fromBooks.getBookEntities),
      map(entities => !!entities[id]),
      take(1)
    );
  }

  /**
   * This method loads a book with the given ID from the API and caches
   * it in the store, returning `true` or `false` if it was found.
   */
  hasBookInApi(id: string): Observable<boolean> {
    return this.googleBooks.retrieveBook(id).pipe(
      map(bookEntity => BookActions.loadBook({ book: bookEntity })),
      tap(action => this.store.dispatch(action)),
      map(book => !!book),
      catchError(() => {
        this.router.navigate(['/404']);
        return of(false);
      })
    );
  }

  /**
   * `hasBook` composes `hasBookInStore` and `hasBookInApi`. It first checks
   * if the book is in store, and if not it then checks if it is in the
   * API.
   */
  hasBook(id: string): Observable<boolean> {
    return this.hasBookInStore(id).pipe(
      switchMap(inStore => {
        if (inStore) {
          return of(inStore);
        }

        return this.hasBookInApi(id);
      })
    );
  }

  /**
   * This is the actual method the router will call when our guard is run.
   *
   * Our guard waits for the collection to load, then it checks if we need
   * to request a book from the API or if we already have it in our cache.
   * If it finds it in the cache or in the API, it returns an Observable
   * of `true` and the route is rendered successfully.
   *
   * If it was unable to find it in our cache or in the API, this guard
   * will return an Observable of `false`, causing the router to move
   * on to the next candidate route. In this case, it will move on
   * to the 404 page.
   */
  canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
    return this.waitForCollectionToLoad().pipe(
      switchMap(() => this.hasBook(route.params['id']))
    );
  }
}

托德·莫托(Todd Motto)在ultimatecourses上也有一篇关于该主题的精彩文章。