【问题标题】:Dispatch action before http call in @ngrx/effects在@ngrx/effects 中 http 调用之前的调度操作
【发布时间】:2018-08-30 09:19:30
【问题描述】:

我想使用 rxjs 的效果进行 http 调用。现在我的问题是我想在 http 调用之前发送另一个动作,比如{ type: LoaderActions.LOADER_START }。所以用户可以在请求 Http 调用时看到加载屏幕,一旦请求完成,我想调度另一个动作 { type: LoaderActions.LOADER_END }

如何使用 rxjs 运算符实现这一点?我很困惑何时在 rxjs 中使用 which 运算符。

auth.effects.ts

import { Injectable } from '@angular/core';
import { Observable, of, concat } from 'rxjs';
import { Action, Store } from '@ngrx/store';
import { Actions, Effect, ofType } from '@ngrx/effects';
import * as AuthActions from './auth.actions';
import * as LoaderActions from '../../loader/store/loader.actions';
import {
  map,
  mergeMap,
  switchMap,
  debounce,
  debounceTime,
  tap,
  startWith
} from 'rxjs/operators';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Router } from '@angular/router';
const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type': 'application/json; charset=utf-8'
  })
};
@Injectable()
export class AuthEffects {
  @Effect()
  signInAction$: Observable<Action> = this.actions$.pipe(
    ofType(AuthActions.TRY_SIGN_IN),
    mergeMap(action =>
      this.http
        .post(
          'http://localhost:8081/auth',
          JSON.stringify({
            username: action['username'],
            password: action['password']
          }),
          httpOptions
        )
        .pipe(
          map(data => {
            if (data['message'] === 'successs') {
              this.router.navigate(['/todo']);
              return { type: AuthActions.SET_AUTH_FLAG, payload: true };
            } else {
              return { type: AuthActions.SET_AUTH_FLAG, payload: false };
            }
          })
        )
    )
  );

  constructor(
    private actions$: Actions,
    private http: HttpClient,
    private router: Router
  ) {}
}

【问题讨论】:

    标签: angular rxjs ngrx-effects


    【解决方案1】:

    您可以使用concat,其中第一个源 Observable 将是加载操作。

    @Effect()
    signInAction$: Observable<Action> = this.actions$.pipe(
      ofType(AuthActions.TRY_SIGN_IN),
      concatMap(action => concat(
        of({ type: LoaderActions.LOADER_START }),
        this.http...
        of({ type: LoaderActions.LOADER_END }),
      ))
    )
    

    concat 运算符将确保按顺序创建操作。

    【讨论】:

    • 答案很好但我想一一调用不并行。我该怎么做?
    • @baj9032 使用concatMap 而不是mergeMap
    【解决方案2】:

    您不应该为您的目的使用额外的操作。只需将loading: boolean 属性添加到您的状态并默认设置为false。当您调度 TRY_SIGN_IN 时,您可以在减速器中将其设置为 true。当 http 成功或失败到达时,您可以通过发送 SET_AUTH_FLAG 操作再次将加载状态设置为 false。

    然后您可以使用适当的选择器选择加载状态,但我想您知道我的意思,并在模板中将它与 Angulars async 管道一起使用。

    【讨论】:

    • 你是对的。但我想在整个应用程序中使用该操作。
    • 啊,你的意思是像所有传出http请求的全局指示器?然后查看 HttpInterceptors 并从那里调度操作,但不是在每个效果中。
    • 我知道这一点,但出于学习目的,我想从效果中调用。
    【解决方案3】:

    您应该为ofType(AuthActions.TRY_SIGN_IN) 创建更多效果侦听器并将此操作映射到发出{ type: LoaderActions.LOADER_START }。那么,在你提供的情况下,你应该

    switchMap(data => {
                if (data['message'] === 'successs') {
                  this.router.navigate(['/todo']);
                  return [{ type: AuthActions.SET_AUTH_FLAG, payload: true }, { type: LoaderActions.LOADER_END }];
                } else {
                  return [{ type: AuthActions.SET_AUTH_FLAG, payload: false }, { type: LoaderActions.LOADER_END }];
                }
              })
    

    通过这种方法,您将获得所需的行为: loader 动作和 http 请求同时执行,当 http 请求完成时,它会发出设置 auth 标志和删除 loader 的动作

    【讨论】:

    • 如果我想在我的情况下一次发送{ type: LoaderActions.LOADER_END },如果条件之前我能做什么?
    • 你可以做实际的.dispatch,但我不建议这样做。为什么你需要在 if 之前发出它?如果您使用 set auth flag 操作发出它,它将正常工作(它将在一个或另一个 if 分支中分派一次)
    猜你喜欢
    • 1970-01-01
    • 2017-06-08
    • 2018-04-18
    • 2017-01-01
    • 2019-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-14
    相关资源
    最近更新 更多