0
votes

I am working on angular5/firebase/ngrx project and I am having a problem catching errors during signup.

If I submit the signup form using an email I know is already in the system, I get the following error in my console: POST https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=AIzaSyB_MH0xkLudBoy2QwVn6jfaFTUjJUylYHs 400 ()

and my effects catches the following error "mail address is already in use by another account." and dispatches signupErrorAction()

But if i immediately resubmit the form, the request does not go anywhere. the call never gets to my effects file or my auth service. I have to get refresh the page to get any calls to work again.

As far as I can narrow it down is the 400 POST error is causing a problem. I will post some code and I could use any suggestions anyone might have. Let me know if you need more info

my-auth-service

public registerWithEmail(payload: any): Promise<any> {
    console.log('signup called');
    return this.afAuth.auth.createUserWithEmailAndPassword(payload.email, 
      payload.password);
}

my-effects

/**
* Register with email and passowrd.
*/
@Effect()
public register: Observable<Action> = this.actions$
    .ofType(actions.ActionTypes.SIGN_UP)
    .map((action: actions.SignUpAction) => action.payload)
    .do(console.log)
    .switchMap(payload => this.authService.registerWithEmail(payload))
    .do(console.log)
    .map(data => new actions.SignUpSuccessAction({ user: data.user }))
    .do(console.log)
    .catch(error => Observable.of(new actions.SignUpErrorAction({ error: error })));

my-signup-method

public register() {

    console.log('register');

    const payload = {
      email: this.email,
      password: this.password,
      displayName: this.name
    }

    // dispatch AuthenticationAction and pass in payload
    this.store.dispatch(new SignUpAction(payload));
}

Update: I tried this and got all kinds of errors

    @Effect()
public register: Observable<Action> = this.actions$
    .ofType(actions.ActionTypes.SIGN_UP)
    .pipe(
    map((action: actions.SignUpAction) => action.payload),
    switchMap(payload => this.authService.registerWithEmail(payload)
        .pipe(
        map(data => new actions.SignUpSuccessAction({ user: data.user })),
        catch(error => Observable.of(new actions.SignUpErrorAction({ error: error })))
        )
    );

The first error I get for: map((action: actions.SignUpAction) => action.payload),

The 'this' context of type 'void' is not assignable to method's 'this' of type 'Observable'.

these are my rxjs imports for the effects file

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/delay';
import 'rxjs/add/operator/do';
import "rxjs/add/operator/debounceTime";
import 'rxjs/add/observable/fromPromise'; 
import 'rxjs/add/observable/of';

Thanks PK

1

1 Answers

1
votes

You need to do the catchError (or catch) within the switchMap, so that the outer Observable does not get canceled on an error.

@Effect()
public register: Observable<Action> = this.actions$
    .ofType(actions.ActionTypes.SIGN_UP)
    .map((action: actions.SignUpAction) => action.payload)
    .switchMap(payload => this.authService.registerWithEmail(payload)
      .map(data => new actions.SignUpSuccessAction({ user: data.user }))
      .catch(error => Observable.of(new actions.SignUpErrorAction({ error: error }))
    )
);

I have also updated your example to use pipeable operators.

@Effect()
public register: Observable<Action> = this.actions$
    .ofType(actions.ActionTypes.SIGN_UP)
    .pipe(
      map((action: actions.SignUpAction) => action.payload),
      switchMap(payload => this.authService.registerWithEmail(payload)
        .pipe(
          map(data => new actions.SignUpSuccessAction({ user: data.user })),
          catchError(error => Observable.of(new actions.SignUpErrorAction({ error: error })))
        )
      )
    );

For this to work you need to add new imports:

import { map, switchMap, catchError } from 'rxjs/operators';