2017-02-21 74 views
8

我对ngrx和Redux风格体系结构很陌生,而且我在理解如何链接动作/效果时遇到问题。一个例子就是实现基本的功能,比如在用户登录后采取行动。我的核心焦点在于,用户登录后采取的行动将取决于应用程序的当前状态 - 用户可能在任何地方在应用程序中提供登录提示/页面时。ngrx链接操作/效果 - 例如,登录然后导航

任何示例我已经看到硬编码效果,一旦用户登录就会发生。在我的情况下,这并不适合如上所述,我并不总是希望发生同样的动作。

以下是包含登录组件的主页的一些示例代码。在这种情况下,我想用户重定向到/买入“一旦他们在已登录

@Component(..) 
export class HomePageComponent { 
    constructor(private store: Store<any>) { 
    } 

    public onLoginSubmitted(username, password) { 
     this.store.dispatch(new loginActions.Login({username, password})); 
     // once this has happened successfully navigate to /buy  
    } 

} 

例效果

@Injectable() 
export class LoginEffects { 

    ......  

    @Effect() 
    login$ = this.actions$ 
     .ofType(loginActions.ActionTypes.LOGIN) 
     .switchMap((data) => { 
      return this.loginService.login(data.payload) 
       .map(() => new loginActions.LoginSuccess({loggedIn: true, isLoggingIn: false})) 
       .catch((error) => { 
        return of(new loginActions.LoginError({loggedIn: false, isLoggingIn: false})); 
       }); 
     }); 
    ...... 
} 

我对你会如何解决这个问题的几点思考 - 但他们都没有感觉正确。这些包括

  • 传递数据与登录有效载荷,以确定要采取的动作下
  • 写作大量的,其在LoginSuccess采取行动,但在一些较高的水平
  • 被过滤通过听写成分特异性作用效果到商店的事件(这是可能/不好的做法?)
  • 从商店读取有关当前登录状态的数据并在组件中采取行动。然而,这种逻辑会立即运行,这可能不会产生预期的效果

任何人都可以指出我正确的道路/目前的例子,这应该如何解决?

+0

有效负载的一部分可能是'redirectRoute',然后登录后,'LoginSuccess'效果将根据'redirectRoute'指定的位置进行导航。你只有一个'loginSuccess'动作,但它可以重定向到任何地方。 – Adam

+0

它可能是 - 但是这假设你将永远想在登录后重定向。这可能不一定是这种情况,例如,您可能想要解雇我喜欢此解决方案的模式 – dannym

回答

9

效果可以在一系列操作上使用mergeMap来触发多个操作。我通常会做的是,传入我希望登录效果也可以发送的附加操作。

@Component(..) 
export class HomePageComponent { 
    constructor(private store: Store<any>) { } 

    public onLoginSubmitted(username, password) { 
     this.store.dispatch(new loginActions.Login({ 
      username, 
      password, 
      onCompleteActions: [new loginActions.Redirect({ route: '/buy' })] })); 
    } 
} 

登录效果然后会使用mergeMap发送任何传入的操作。您仍然必须为每个操作创建一个效果,但现在可以构建更多可重用操作,例如重定向操作。

@Injectable() 
export class LoginEffects { 

    ......  

    @Effect() 
    login$ = this.actions$ 
     .ofType(loginActions.ActionTypes.LOGIN) 
     .switchMap((data) => { 
      let mappedActions = [new loginActions.LoginSuccess({loggedIn: true, isLoggingIn: false})]; 
      if (data.payload.onCompleteActions) 
       mappedActions = mappedActions.concat(data.payload.onCompleteActions); 

      return this.loginService.login(data.payload) 
       .mergeMap(mappedActions) 
       .catch((error) => { 
        return of(new loginActions.LoginError({loggedIn: false, isLoggingIn: false})); 
       }); 
     }); 

    ...... 
} 

我喜欢这个解决方案是什么,它是可升级的未来,因为任何操作都可以通过在,而且也没有开关或的if-else树来决定做什么。您还可以传入多个操作,因此不需要构建“redirectAndCloseModal”操作,您可以传入redirect以及closeModal

this.store.dispatch(new loginActions.Login({ 
    username, 
    password, 
    onCompleteActions: [new loginActions.Redirect({ route: '/buy' }), new modalActions.CloseModal()] })); 

你可以阅读一些有关dispatching multiple Actions from one @Effect() at this github issue.

编辑:关于你的评论,它不只是路线的变化,可能使你想停止登录,它可以是任何东西,如关闭一个模式,并没有通用的方式来观察。您可以添加一些东西到您的商店,例如runLoginActions布尔值,然后在登录请求返回后从商店中读取该数据。

然后在您的login$效果中,您可以从商店中读取它以决定是否运行其他操作。

@Effect() 
login$: Observable<Action> = this.actions$ 
    .ofType(auth.actionTypes.LOGIN) 
    .map((action: auth.LoginAction) => action.payload) 
    .switchMap((user) => { 
     let successAction = new loginActions.LoginSuccess({loggedIn: true, isLoggingIn: false}); 
     let mappedActions = [successAction]; 
     if (data.payload.onCompleteAction) 
      mappedActions.push(data.payload.onCompleteAction); 

     return this.authService.login(user.username, user.password) 
      .withLatestFrom(this.store) 
      .mergeMap(([result, store]) => store.runLoginActions ? 
       [mappedActions] : 
       [successAction] 
      ) 
      .catch((err) => { 
       try { 
        return of(new auth.NetworkFailureAction(err[0].code)); 
       } catch (e) { 
        throw `Array not in the expected format of [{ code: "", message: "" }]`; 
       } 
      }); 
    }); 

注意,在你减速的loginSuccess,你应该设置你的runLoginActions = true,它应该是默认启动为真,只有当路由改变被关闭或部分被破坏。

请记住,这是一个通用的解决方案,你会需要自己添加的行动,更新减速等

这是一个更大的手工工艺,但由于你的情况不想要执行操作非常具体,可能需要这样做。

+0

。我有一个问题,如果这些行动可以被取消?例如,用户厌倦了等待登录完成并在其他地方导航,我们可能想要取消一旦登录呼叫返回时发生的重定向 – dannym

+0

您是否想要取消登录?服务器仍然会被告知用户已经登录,但是现在角度客户端已经忽略了该响应并且不与服务器同步。无论哪种方式,我正在考虑这一点,因为我不确定答案 – Adam

+0

您可能不想取消登录,但是您可能想要在稍后取消重定向。这是一种情况 - 用户转到主页。在主页上有一个登录表单和一个FAQ按钮。用户输入详细信息并单击登录。处理请求时,他们感到无聊,然后点击常见问题按钮,进入常见问题解答页面并开始阅读常见问题解答。一旦您的登录请求返回,您现在不想重定向用户。 – dannym