import { FormAction, FormActionTypes, FormAddError, FormChangeAction, FormClearErrorsAction, FormInitAction, FormRemoveAction, FormRemoveErrorAction, FormStatusChangeAction } from './form.actions'; import { hasValue } from '../empty.util'; import { isEqual, uniqWith } from 'lodash'; export interface FormError { message: string; fieldId: string; fieldIndex: number; } export interface FormEntry { data: any; valid: boolean; errors: FormError[]; } export interface FormState { [formId: string]: FormEntry; } const initialState: FormState = Object.create(null); export function formReducer(state = initialState, action: FormAction): FormState { switch (action.type) { case FormActionTypes.FORM_INIT: { return initForm(state, action as FormInitAction); } case FormActionTypes.FORM_CHANGE: { return changeDataForm(state, action as FormChangeAction); } case FormActionTypes.FORM_REMOVE: { return removeForm(state, action as FormRemoveAction); } case FormActionTypes.FORM_STATUS_CHANGE: { return changeStatusForm(state, action as FormStatusChangeAction); } case FormActionTypes.FORM_ADD_ERROR: { return addFormErrors(state, action as FormAddError) } case FormActionTypes.FORM_REMOVE_ERROR: { return removeFormError(state, action as FormRemoveErrorAction) } case FormActionTypes.FORM_CLEAR_ERRORS: { return clearsFormErrors(state, action as FormClearErrorsAction) } default: { return state; } } } function addFormErrors(state: FormState, action: FormAddError) { const formId = action.payload.formId; if (hasValue(state[formId])) { const error: FormError = { fieldId: action.payload.fieldId, fieldIndex: action.payload.fieldIndex, message: action.payload.errorMessage }; return Object.assign({}, state, { [formId]: { data: state[formId].data, valid: state[formId].valid, errors: state[formId].errors ? uniqWith(state[formId].errors.concat(error), isEqual) : [].concat(error), } }); } else { return state; } } function removeFormError(state: FormState, action: FormRemoveErrorAction) { const formId = action.payload.formId; const fieldId = action.payload.fieldId; const fieldIndex = action.payload.fieldIndex; if (hasValue(state[formId])) { const errors = state[formId].errors.filter((error) => error.fieldId !== fieldId || error.fieldIndex !== fieldIndex); const newState = Object.assign({}, state); newState[formId] = Object.assign({}, state[formId], {errors}); return newState; } else { return state; } } function clearsFormErrors(state: FormState, action: FormClearErrorsAction) { const formId = action.payload.formId; if (hasValue(state[formId])) { const errors = []; const newState = Object.assign({}, state); newState[formId] = Object.assign({}, state[formId], {errors}); return newState; } else { return state; } } /** * Init form state. * * @param state * the current state * @param action * an FormInitAction * @return FormState * the new state, with the form initialized. */ function initForm(state: FormState, action: FormInitAction): FormState { const formState = { data: action.payload.formData, valid: action.payload.valid, errors: [] }; if (!hasValue(state[action.payload.formId])) { return Object.assign({}, state, { [action.payload.formId]: formState }); } else { const newState = Object.assign({}, state); newState[action.payload.formId] = Object.assign({}, newState[action.payload.formId], formState); return newState; } } /** * Set form data. * * @param state * the current state * @param action * an FormChangeAction * @return FormState * the new state, with the data changed. */ function changeDataForm(state: FormState, action: FormChangeAction): FormState { if (hasValue(state[action.payload.formId])) { const newState = Object.assign({}, state); newState[action.payload.formId] = Object.assign({}, newState[action.payload.formId], { data: action.payload.formData, valid: state[action.payload.formId].valid } ); return newState; } else { return state; } } /** * Set form status. * * @param state * the current state * @param action * an FormStatusChangeAction * @return FormState * the new state, with the status changed. */ function changeStatusForm(state: FormState, action: FormStatusChangeAction): FormState { if (!hasValue(state[action.payload.formId])) { return Object.assign({}, state, { [action.payload.formId]: { data: state[action.payload.formId].data, valid: action.payload.valid } }); } else { const newState = Object.assign({}, state); newState[action.payload.formId] = Object.assign({}, newState[action.payload.formId], { data: state[action.payload.formId].data, valid: action.payload.valid } ); return newState; } } /** * Remove form state. * * @param state * the current state * @param action * an FormRemoveAction * @return FormState * the new state, with the form initialized. */ function removeForm(state: FormState, action: FormRemoveAction): FormState { if (hasValue(state[action.payload.formId])) { const newState = Object.assign({}, state); delete newState[action.payload.formId]; return newState; } else { return state; } }