0

我正在开发一个 Angular 8 应用程序。我想使用 NgRx 存储和使用自定义异步验证器的反应式表单来显示表单错误。

登录组件.ts

@Component({
  selector: 'auth-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss'],
})
export class LoginComponent implements OnInit {
  public transitionController: TransitionController = new TransitionController(
    true,
  );
  public form: FormGroup;
  public pageState$: Observable<ILoginPageState>;
  public formSubmitted: boolean = false;

  constructor(
    private _formBuilder: FormBuilder,
    private _store: Store<IAppState>,
  ) {
    this.pageState$ = this._store.select(selectLoginPageState) as Observable<
      ILoginPageState
    >;
    this._buildForm();
  }

  ngOnInit() {
    this._animatePage();
  }

  public onFormSubmit() {
    this.formSubmitted = true;

    if (this.form.invalid) {
      return;
    }

    this._store.dispatch(Login(this.form.value));
  }

  private _buildForm() {
    this.form = this._formBuilder.group({
      email: this._formBuilder.control(
        null,
        [Validators.required, Validators.email],
        [this.test.bind(this)],
      ),
      password: this._formBuilder.control(null, Validators.required),
    });
  }

  private test(control: AbstractControl) {
    return this._store.select(selectLoginErrorMessage).pipe(
      tap(() => console.log('executing')),
      map(value => ({
          foo: true
      })),
    );
  }

  private _animatePage() {
    this.transitionController.animate(
      new Transition(EAnimationType.FadeUp, 500, TransitionDirection.In),
    );
  }
}

登录页面.effects.ts

@Injectable()
export class LoginEffects {
  constructor(
    private _actions$: Actions,
    private _authService: AuthenticationSevice,
    private _router: Router,
    private _modalService: SuiModalService,
    private _store: Store<IAppState>,
  ) {}

  Login$ = createEffect(() => {
    return this._actions$.pipe(
      ofType(AuthActions.Login),
      tap(() => this._store.dispatch(ShowPageLoader())),
      switchMap((credentials: ILoginCredentials) =>
        this._authService.login(credentials).pipe(
          map((response: ILoginResponse) => AuthActions.LoginSuccess(response)),
          catchError((response: HttpErrorResponse) => {
            let validationErrors: ValidationErrors;

            switch (response.status) {
              case HttpStatusCode.BAD_REQUEST:
                validationErrors = {
                  error: {
                    validationErrors: response.error,
                    generalError:
                      'Oops! We found some errors with your provided details.',
                  },
                };
                break;
              case HttpStatusCode.NOT_FOUND:
                validationErrors = {
                  error: {generalError: 'Email or password is incorrect.'},
                };
                break;
            }

            return of(AuthActions.LoginFailure(validationErrors));
          }),
          finalize(() => this._store.dispatch(HidePageLoader())),
        ),
      ),
    );
  });

  LoginSuccess$ = createEffect(
    () => {
      return this._actions$.pipe(
        ofType(AuthActions.LoginSuccess),
        tap(() => {
          this._modalService.open(
            new ModalComponent<IModalContext>(undefined, {
              title: 'Login successful',
              imageSrc: 'assets/images/modal/login-successful.png',
            }),
          );

          this._router.navigateByUrl('/home');
        }),
      );
    },
    {dispatch: false},
  );
}

这里的主要问题在于我的test方法。我希望在{ foo : true}错误字段上设置,但它永远不会发生。我在谷歌上搜索了很多,我发现的一个解决方案是在first()里面添加方法,pipe()以便我的 observable 完成。它起作用了,但只是第一次。另外,提交表单时从未调用过异步验证器。

我在互联网上找到的所有示例都是使用 Http 调用。我知道它在请求完成时完成了 observable 但在我的情况下,Http 调用正在我的内部处理login-page.effects.ts

有没有更好的方法呢?还是我需要一些我不熟悉的 RxJs 运算符?

4

3 回答 3

0

我会说这是一个非常根本的错误。您首先需要做的是了解 pipe() 和 subscribe() 之间的区别。

这里有一个有用的链接:Difference between the methods .pipe() and .subscribe() on a RXJS observable

基本上,您的代码的问题在于您正在对 observable 进行管道操作,这意味着您的操作只会执行一次,并且不会监视更改。

您需要使用下面显示的代码实际订阅选择操作:

this.error$ = this.store.select(selectLoadingErrors);
this.error$.subscribe(data=>{
//do your code here

});
于 2019-09-02T18:05:26.860 回答
0

函数中的maprxjs 运算符test()没有返回您认为的内容,因为它foo被解释为标签,而不是您想要的对象文字中的键。有关更多说明,请参阅文档

有两种方法可以获得正确的语法。

1.

map(value => {
  return {foo: true};
}),

2.

map(value => ({foo: true}),
于 2019-08-31T16:24:33.910 回答
0

不要返回this._store.select(),而是尝试连接到actions流中侦听多个动作完成事件AuthActions.LoginFailureAuthActions.LoginSuccess

this.actions$.pipe(
        ofType(AuthActions.LoginFailure, AuthActions.LoginSuccess),
        switchMap(x => {
            if (x.type == AuthActions.LoginFailure)
                return this._store.pipe(
                    select(selectLoginErrorMessage),
                    map(msg => ({ foo: true })),
                    take(1)
                );
            return true;
        })
    )

store.select立即开火,不等待效果结束。

现在对于另一个问题 -

提交表单时从未调用过异步验证器。

验证是在控制级别 ( email) 上配置的,因此不会被隐式触发。理想情况下,如果任何控件验证中出现错误,则不应允许提交您的表单。

于 2019-09-01T13:06:38.607 回答