阻止路由器导航以防万一 failure/error

Preventing router navigation in case of an effect failure/error

我正在迁移以使用 ngrx Effects,但我遇到了以下问题:因为 dispatch returns void 我不确定如何如果我的 ngrx 效果发生错误(例如来自 this.userAccountService.updateFirstName(...),则阻止路由器导航。

来自我的组件class:

  updateFirstName() {
    this.formStatus.submitted = true;
    if (this.formStatus.form.valid) {
      //Dispatch from the store
      this.store.dispatch(new UpdateFirstNameAction(this.formStatus.form.value.firstName));
      //Undesired behavior: will navigate even in case of error/failure!
      this.router.navigate(['/dashboard/useraccount']);
    }
  }

我的效果class:

  @Effect()
  updateFirstName$: Observable<Action> = this.actions$
    .ofType(currentUserAccount.ActionTypes.UPDATE_FIRST_NAME)
    .map((action: UpdateFirstNameAction) => action.payload)
    .switchMap(firstName =>
      this.userAccountService
        .updateFirstName(firstName)
        .map(() => new UpdateFirstNameSuccessAction(firstName))
        //I obviously can't navigate from here
    );

我不确定使用路由器从效果中导航是否是一个好习惯(甚至可能)。

如果我的效果出现错误,有人可以建议一个干净的解决方案来防止路由器导航吗?

您可以选择以下两种方式之一。

  1. 使用 Effect,捕捉 SUCCESS 动作然后重定向:

    @Effect() someeffect$: Observable<Action> = this.actions$
        .ofType(currentUserAccount.ActionTypes.UPDATE_FIRST_NAME_SUCCESS)                
        .do(action => /* do redirect (action.payload) */);
    
  2. 有订阅:

    假设这是你的状态:

    {
        account: {
            username: null
        }
    }
    

    组件:

    var username$ = this.store.select(account => account.username);
    
    username$.subscribe(user => {
        // the user have changed due to reducer who is getting the action with new user.
        // do here redirect...
    });
    

我建议使用 @ngrx/router-store 模块。有了它,您可以创建路由操作,使用它,您有几个选择。

您可以从一个效果中发出多个动作。因此,您的效果可以使用 concatMap 发出两个动作:您的成功动作和路由动作(使用 go 动作创建器创建):

import { go } from '@ngrx/router-store';
import 'rxjs/add/operator/concatMap';

@Effect()
updateFirstName$: Observable<Action> = this.actions$
  .ofType(currentUserAccount.ActionTypes.UPDATE_FIRST_NAME)
  .map((action: UpdateFirstNameAction) => action.payload)
  .switchMap(firstName => this.userAccountService
    .updateFirstName(firstName)
    .concatMap(() => [
      new UpdateFirstNameSuccessAction(firstName),
      go(['/dashboard/useraccount'])
    ])
  );

但是,您可能还想考虑使用仅管理路由的单独效果器:

import { go } from '@ngrx/router-store';
import 'rxjs/add/operator/mapTo';

@Effect()
updateFirstRouting$: Observable<Action> = this.actions$
  .ofType(currentUserAccount.ActionTypes.UPDATE_FIRST_NAME_SUCCESS)
  .mapTo(go(['/dashboard/useraccount']);

要考虑的另一件事是单个效果可以响应多个动作,因此您可以使用 table 的简单 action-type-to-route 映射来执行如下操作:

import { go } from '@ngrx/router-store';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';

simpleRoutes: { [type: string]: string } = {
  [currentUserAccount.ActionTypes.UPDATE_FIRST_NAME_SUCCESS]: '/dashboard/useraccount'
};

@Effect()
simpleRouting$: Observable<Action> = this.actions$
  .map((action) => this.simpleRoutes[action.type])
  .filter(Boolean)
  .map((route) => go([route]));

无论您选择哪个选项,使用在效果中启动的导航路由操作都会使事情变得更容易测试table。编写效果测试以确保当效果接收到特定操作时,它会发出适当的操作很容易。有关测试效果的更多信息 here