Reputation: 13
Excuse my ignorance, I am fairly new to the reactive concepts.
My issue is with not knowing how to deal loading a Ionic 2 loader or an Ionic 2 alert based on the stores current state.
I have been able to achieve the loader behaviour I need by subscribing to the store slice it is reacting to. Although when it comes to an alert (thrown on a catched error), it never fires in the subscription block.
Any help pointing out a better direction, or what I have missed would be greatly appreciated.
This code is from the signin modals view.
signin(user) {
this.submitAttempt = true;
if (this.signinForm.valid) {
let loader = this.loadingCtrl.create({
content: "Signing In..."
});
let auth;
let signinSub = this.store.select(s => auth = s.auth).subscribe(() => {
if (auth.state) {
loader.dismiss();
} else if (auth.error) {
let alert = this.alertCtrl.create({
title: "Error",
subTitle: auth.error,
buttons: ['OK']
});
loader.dismiss();
alert.present();
}
});
loader.present();
this.store.dispatch(UserActions.UserActions.signinUser(user));
}
}
Effect
@Effect() signinUser$ = this.actions$
.ofType(UserActions.ActionTypes.SIGNIN_USER)
.map(toPayload)
.switchMap(user => {
return Observable.fromPromise(this.userService.signinUser(user))
.map(result => {
return ({ type: "GET_USER", payload: user});
})
.catch(err => {
return Observable.of({ type: "SIGNIN_USER_FAILED", payload: err });
});
});
Service
signinUser(user): Promise<any> {
return <Promise<any>>firebase.auth()
.signInWithEmailAndPassword(user.email, user.password);
}
Reducer
export const UserReducer: ActionReducer<Auth> = (state: Auth = initialState, action: Action) => {
switch(action.type) {
case UserActions.ActionTypes.SIGNIN_USER:
return state;
case UserActions.ActionTypes.SIGNIN_USER_FAILED:
return Object.assign(state, { apiState: "Failed", error: action.payload.message });
case UserActions.ActionTypes.STARTED_SIGNIN:
return Object.assign(state, { requested: true });
case UserActions.ActionTypes.GET_USER:
return Object.assign(state, { apiState: "Success", error: ""});
case UserActions.ActionTypes.GET_USER_SUCCESS:
return Object.assign({ user: action.payload.val() }, state, { state: true });
default:
return state;
};
}
store
export interface Auth {
state: boolean,
requested: boolean,
apiState: string,
error: {},
user?: {}
}
export interface AppState {
auth: Auth;
}
Upvotes: 1
Views: 775
Reputation: 33345
I just have a loadingState in my store and then I load and unload the spinner/loading UI based on that state.
I have a complete project here showing how I manage the state and the UI
https://github.com/aaronksaunders/ngrx-simple-auth
/**
* Keeping Track of the AuthenticationState
*/
export interface AuthenticationState {
inProgress: boolean; // are we taking some network action
isLoggedIn: boolean; // is the user logged in or not
tokenCheckComplete: boolean; // have we checked for a persisted user token
user: Object; // current user | null
error?: Object; // if an error occurred | null
}
and then in the different states, AuthActions.LOGIN
case AuthActions.LOGIN: {
return Object.assign({}, state, {inProgress: true, isLoggedIn: false, error: null})
}
and then, AuthActions.LOGIN_SUCCESS
case AuthActions.LOGIN_SUCCESS: {
return Object.assign({}, state, {inProgress: false, user: action.payload, isLoggedIn: true})
}
here is how we handle it in the LoginPage
var dispose = this.store.select('authReducer').subscribe(
(currentState: AuthenticationState) => {
console.log("auth store changed - ", currentState);
if (currentState.user) {
dispose.unsubscribe();
this.nav.setRoot(HomePage, {});
}
// this is where the magic happens...
this.handleProgressDialog(currentState);
this.error = currentState.error
},
error => {
console.log(error)
}
);
}
how we handle loading
/**
*
* @param _currentState
*/
handleProgressDialog(_currentState) {
if (_currentState.inProgress && this.loading === null) {
this.loading = this.loadingCtrl.create({
content: "Logging In User..."
});
this.loading.present()
}
if (!_currentState.inProgress && this.loading !== null) {
this.loading && this.loading.dismiss();
this.loading = null;
}
}
Upvotes: 1
Reputation: 1
I use Ionic 2 with ngrx too and so far as I know, LoadingController and AlertController don't provide any observable or promise. So I think the best you can do is what you're doing now by subscribing its state and do some condition based on its state.
OR you can get rid LoadingController replace it with ion-spinner:
<ion-spinner [disabled]="isLoading$ | async"></ion-spinner>
And replace AlertController with some label :
<span>{{errorMessage$ | async}}</span>
Upvotes: 0