I have problem when i dispatch the login action for second or more time. With first request evrything is ok if login is successful token is acquired and user get re-routed to main page. If is unsucesfull u get message that ur login was wrong and should try again but if try to log for the second time none request is send.
The method which is call after button "Login" was clicked is method onLogin in class "AccountLoginComponent"
Reducer
import {createEntityAdapter, EntityAdapter, EntityState} from '@ngrx/entity';
import {ActionListModel} from '../models/actionListModel';
import {ActionDetailModel} from '../models/actionDetailModel';
import {createReducer, on} from '@ngrx/store';
import * as ActionActions from './action.actions';
export interface ActionState extends EntityState<ActionListModel> {
actionDetail: ActionDetailModel,
messageActionCreation: string,
actionDetailStatus: string,
actionListStatus: string,
actionCreationStatus: string,
}
export function selectActionId(action: ActionListModel): number {
//In this case this would be optional since primary key is id
return action.idAction;
}
// we will add entity adapter to our init state
export const adapter: EntityAdapter<ActionListModel> = createEntityAdapter<ActionListModel>({
selectId: selectActionId
});
// our init state is init state of EntityState
export const initialState: ActionState = {
...adapter.getInitialState(),
actionDetail: null,
messageActionCreation: "",
actionListStatus: "",
actionDetailStatus: "",
actionCreationStatus: "",
};
const _actionReducer = createReducer(
initialState,
on(ActionActions.loadActionsPageSuccess, (state, {actionInList}) => {
return adapter.addAll(actionInList, state);
}),
on(ActionActions.loadActionsPageSuccess, (state, {status}) => ({
...state, actionListStatus: status
})),
on(ActionActions.loadActionDetailSuccess, (state, {actionDetail}) => ({
...state, actionDetail: actionDetail, actionDetailStatus: status
})),
on(ActionActions.createAirsoftActionSuccess, (state, {action, status}) => ({
...state, actionDetail: action, actionCreationStatus: status,
}))
);
export function actionReducer(state, action) {
return _actionReducer(state, action);
}
// get the selectors
const {
selectIds,
selectEntities,
selectAll,
selectTotal,
} = adapter.getSelectors();
export const selectActionIds = selectIds;
export const selectActionsEntities = selectEntities;
export const selectAllActions = selectAll;
export const selectActionsTotal = selectTotal;
Actions
import {createAction, props} from '@ngrx/store';
import {AccountModel} from '../models/account.model';
export enum AccountActionsTypes {
AccountLogin = '[Login Page] Account Login',
AccountLoginSuccess = '[Login Page] Account Login Success',
AccountLoginFailed = '[Login Page] Account Login Failed',
AccountLogout = '[Header] Account Logout',
AccountActivation = '[Activation page] Account Activation',
AccountActivationSuccess = '[Activation page] Account Activation Success',
AccountActivationFailed = '[Activation page] Account Activation Failed',
}
export const login = createAction(AccountActionsTypes.AccountLogin, props<{ email: string; password: string }>());
export const loginSuccess = createAction(AccountActionsTypes.AccountLoginSuccess, props< { account : AccountModel, token : string, message: string, status: string}>());
export const loginFailed = createAction(AccountActionsTypes.AccountLoginFailed);
export const logout = createAction(AccountActionsTypes.AccountLogout);
export const accountActivation = createAction(AccountActionsTypes.AccountActivation, props<{formula : string}>());
export const accountActivationSuccess = createAction(AccountActionsTypes.AccountActivationSuccess, props<{message : string}>());
export const accountActivationFailed = createAction(AccountActionsTypes.AccountActivationFailed, props<{error : string}>());
Effect
login$ = createEffect(() =>
this.actions$.pipe(
tap( kek => {/*console.log(kek)*/}),
ofType(AccountActionsTypes.AccountLogin),
exhaustMap(({email: email, password: password}) => this.accountService.login(email,password)
.pipe(
map((response : { body: {account : AccountModel, token : string, message: string, status: string}}) =>
({type: AccountActionsTypes.AccountLoginSuccess, account: response.body.account, token: response.body.token, message: response.body.message, status: response.body.status })),
catchError(error => of({type: AccountActionsTypes.AccountLoginFailed, error: error })),
tap(response => {
/*console.log(response)*/;
}),
))
));
Service
private _options = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) };
login(email: string, password: string): Observable<Object> {
return this.http.post('http://localhost:8080/user/login', {email: email, password: password}, this._options); }
AccountLoginComponent
onLogin() {
this.store.dispatch(login({email : this.loginForm.value.email, password: sha1(this.loginForm.value.password)}));
this.loginResult$ = this.store.pipe(select(selectLoginStatus));
this.loginResultSub = this.loginResult$.subscribe(({loginStatus, message}) => {
this.isSubscribe = true;
this.message = message;
if(loginStatus === 'success') {
this.router.navigate(['/home']);
}
});
}
The problem was in the effect because if the login was wrong i set token and account in my state to undefinied. I fix it like this:
Repaired Effect
login$ = createEffect(() =>
this.actions$.pipe(
ofType(AccountActionsTypes.AccountLogin),
exhaustMap(({email: email, password: password}) => this.accountService.login(email,password)
.pipe(
map((response : { body: {account : AccountModel, token : string, message: string, status: string}}) => {
if(response.body.token !== undefined) {
return {type: AccountActionsTypes.AccountLoginSuccess, account: response.body.account, token: response.body.token, message: response.body.message, status: response.body.status };
} else {
return {type: AccountActionsTypes.AccountLoginWrong, message: response.body.message, status: response.body.status };
}
}),
catchError(error => of({type: AccountActionsTypes.AccountLoginFailed, error: error })),
)
)
));
New Action
export const loginWrong = createAction(AccountActionsTypes.AccountLoginWrong, props< {message: string, status: string}>());
Reducer
const _accountReducer = createReducer(
initialState,
on(AccountActions.loginSuccess, (state, { account, token, message, status } ) => ({
...state, account: account, token: token, message: message, loginStatus: status
})),
on(AccountActions.loginWrong, (state, { message, status } ) => ({
...state, message: message, loginStatus: status
})),
on(AccountActions.logout, (state) => ({
...state, account: null, message: "", token: ""
})),
on(AccountActions.accountActivationSuccess, (state, { message } ) => ({
...state, message: message
})),
);