下一个叫做inmediatly的回调

时间:2017-02-08 17:55:48

标签: angular rxjs ngrx

我已经构建了一个Store界面:

export interface IStore {
  user: IUser;
}

其中IUser是:

export interface IUser {
    id: string;
    name: string;
    username: string;
    customer: string;
}

在我的组件中,我创建了对IStore.user的订阅:

export class LoginComponent implements OnInit {

    private user$: Observable<IUser>;
    private userSub: Subscription;

    constructor(private store$: Store<IStore>)
    {
        this.user$ = this.store$.select(state => state.user);
    }

    ngOnInit():void {
        this.userSub = this.user$.subscribe(
            (user: IUser) => {
                this.router.navigate(['/app']);  ((((1))))
            },
            (error: any) => {
                this.addAlert(error.message);
            }
        );
    }

    ngOnDestroy():void {
        this.userSub.unsubscribe();
    }

    public login():void {
        this.store$.dispatch({ type: 'USER_REDUCER_USER_LOGIN' });
    }
}

目前,已达到((((1))))代码,仅建立了inmediatly订阅。然而,当((((1)))) callback login()`方法。

时,期望的行为是达到'USER_REDUCER_USER_LOGIN' action is dispatched in

这是我的UserReducer.ts

export class UserReducer {
  private static reducerName = 'USER_REDUCER';

  public static reducer(user = initialUserState(), {type, payload}: Action) {
    if (typeof UserReducer.mapActionsToMethod[type] === 'undefined') {
      return user;
    }

    return UserReducer.mapActionsToMethod[type](user, type, payload);
  }

    // tslint:disable-next-line:member-ordering
    /**
     * Default reducer type. I want all sources.
     */
    public static USER_LOGIN = `${UserReducer.reducerName}_USER_LOGIN`;

    /**
     * User login success.
     */
    public static USER_LOGIN_SUCCESS = `${UserReducer.reducerName}_USER_LOGIN_SUCCESS`;
    private static userLoginSuccess(sourcesRdx, type, payload) {
        return Object.assign(<IUser>{}, sourcesRdx, payload);
    }

    /**
     * User login fails.
     */
    public static USER_LOGIN_FAILED = `${UserReducer.reducerName}_USER_LOGIN_FAILED`;
    private static userLoginFailed(sourcesRdx, type, payload) {
        return Object.assign(<IUser>{}, sourcesRdx, payload);
    }

  // ---------------------------------------------------------------

  // tslint:disable-next-line:member-ordering
  private static mapActionsToMethod = {
      [UserReducer.USER_LOGIN_SUCCESS]: UserReducer.userLoginSuccess,
      [UserReducer.USER_LOGIN_FAILED]: UserReducer.userLoginFailed,
  };
}

initialUserState()是:

export function initialUserState(): IUser {
    return {
        id: '',
        name: '',
        username: '',
        customer: ''
    };
};

有什么想法吗?

修改

@Injectable()
export class UserEffects {
  constructor(
    private _actions$: Actions,
    private _store$: Store<IStore>,
    private _userService: UsersService,
  ) { }

  @Effect({ dispatch: true }) userLogin$: Observable<Action> = this._actions$
    .ofType('USER_REDUCER_USER_LOGIN')
    .switchMap((action: Action) =>
      this._userService.checkPasswd(action.payload.username, action.payload.password)
        .map((user: any) => {
          return { type: 'USER_REDUCER_USER_LOGIN_SUCCESS', payload: user };
        })
        .catch((err) => {
          return Observable.of({
            type: 'USER_REDUCER_USER_LOGIN_ERROR',
            payload: { error: err }
          });
        })
    );
}

1 个答案:

答案 0 :(得分:1)

您所看到的是预期的行为。当您订阅商店时,它会立即发出当前状态,如果尚未收到操作,则该状态将是初始状态。

最简单的解决方案是使用filter运算符:

import 'rxjs/add/operator/filter';

ngOnInit():void {
  this.userSub = this.user$
    .filter(Boolean)
    .subscribe(
      (user: IUser) => { this.router.navigate(['/app']); },
      (error: any) => { this.addAlert(error.message); }
    );
}

应用过滤器后,user$观察者不会发出空值,因此导航不会发生,直到用户登录为止。

您可以使用first运算符进一步简化实施。当观察完成或错误时,订阅者将自动取消订阅。通过使用first运算符,组合的observable将在第一次发射后完成,因此不需要手动取消订阅,并且可以删除userSub属性和ngOnDestroy方法:

import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/first';

ngOnInit(): void {
  this.user$
    .filter(Boolean)
    .first()
    .subscribe(
      (user: IUser) => { this.router.navigate(['/app']); },
      (error: any) => { this.addAlert(error.message); }
    );
}

另一种解决方案是使用@ngrx/effects来管理路由。您可能认为LoginComponent的责任是获取凭据并对用户进行身份验证,但可能不是该组件负责决定导航的位置。如果您想要其他东西负责导航,您可以创建一个侦听USER_REDUCER_USER_LOGIN_SUCCESS的路由效果。

您可能还需要考虑使用@ngrx/router-store在商店中包含路由器状态 - 以便于进行时间旅行调试和撤消导航更改。有关详细信息,请参阅this answer