ngrx-forms
Version:
Proper integration of forms in Angular 4 applications using ngrx
1,322 lines (1,279 loc) • 148 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, forwardRef, Directive, HostListener, Input, PLATFORM_ID, Optional, Inject, Host, Self, HostBinding, EventEmitter, Output, NgModule } from '@angular/core';
import { isPlatformBrowser, DOCUMENT } from '@angular/common';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import * as i1 from '@ngrx/store';
import { ActionsSubject } from '@ngrx/store';
// NOTE: the explicit type declaration for the `TYPE` properties is required
// for the output declarations to properly use the literal string type instead
// of just `string`
class SetValueAction {
constructor(controlId, value) {
this.controlId = controlId;
this.value = value;
this.type = SetValueAction.TYPE;
}
}
SetValueAction.TYPE = 'ngrx/forms/SET_VALUE';
class SetErrorsAction {
constructor(controlId, errors) {
this.controlId = controlId;
this.errors = errors;
this.type = SetErrorsAction.TYPE;
}
}
SetErrorsAction.TYPE = 'ngrx/forms/SET_ERRORS';
class SetAsyncErrorAction {
constructor(controlId, name, value) {
this.controlId = controlId;
this.name = name;
this.value = value;
this.type = SetAsyncErrorAction.TYPE;
}
}
SetAsyncErrorAction.TYPE = 'ngrx/forms/SET_ASYNC_ERROR';
class ClearAsyncErrorAction {
constructor(controlId, name) {
this.controlId = controlId;
this.name = name;
this.type = ClearAsyncErrorAction.TYPE;
}
}
ClearAsyncErrorAction.TYPE = 'ngrx/forms/CLEAR_ASYNC_ERROR';
class StartAsyncValidationAction {
constructor(controlId, name) {
this.controlId = controlId;
this.name = name;
this.type = StartAsyncValidationAction.TYPE;
}
}
StartAsyncValidationAction.TYPE = 'ngrx/forms/START_ASYNC_VALIDATION';
class MarkAsDirtyAction {
constructor(controlId) {
this.controlId = controlId;
this.type = MarkAsDirtyAction.TYPE;
}
}
MarkAsDirtyAction.TYPE = 'ngrx/forms/MARK_AS_DIRTY';
class MarkAsPristineAction {
constructor(controlId) {
this.controlId = controlId;
this.type = MarkAsPristineAction.TYPE;
}
}
MarkAsPristineAction.TYPE = 'ngrx/forms/MARK_AS_PRISTINE';
class EnableAction {
constructor(controlId) {
this.controlId = controlId;
this.type = EnableAction.TYPE;
}
}
EnableAction.TYPE = 'ngrx/forms/ENABLE';
class DisableAction {
constructor(controlId) {
this.controlId = controlId;
this.type = DisableAction.TYPE;
}
}
DisableAction.TYPE = 'ngrx/forms/DISABLE';
class MarkAsTouchedAction {
constructor(controlId) {
this.controlId = controlId;
this.type = MarkAsTouchedAction.TYPE;
}
}
MarkAsTouchedAction.TYPE = 'ngrx/forms/MARK_AS_TOUCHED';
class MarkAsUntouchedAction {
constructor(controlId) {
this.controlId = controlId;
this.type = MarkAsUntouchedAction.TYPE;
}
}
MarkAsUntouchedAction.TYPE = 'ngrx/forms/MARK_AS_UNTOUCHED';
class FocusAction {
constructor(controlId) {
this.controlId = controlId;
this.type = FocusAction.TYPE;
}
}
FocusAction.TYPE = 'ngrx/forms/FOCUS';
class UnfocusAction {
constructor(controlId) {
this.controlId = controlId;
this.type = UnfocusAction.TYPE;
}
}
UnfocusAction.TYPE = 'ngrx/forms/UNFOCUS';
class MarkAsSubmittedAction {
constructor(controlId) {
this.controlId = controlId;
this.type = MarkAsSubmittedAction.TYPE;
}
}
MarkAsSubmittedAction.TYPE = 'ngrx/forms/MARK_AS_SUBMITTED';
class MarkAsUnsubmittedAction {
constructor(controlId) {
this.controlId = controlId;
this.type = MarkAsUnsubmittedAction.TYPE;
}
}
MarkAsUnsubmittedAction.TYPE = 'ngrx/forms/MARK_AS_UNSUBMITTED';
class AddArrayControlAction {
constructor(controlId, value, index) {
this.controlId = controlId;
this.value = value;
this.index = index;
this.type = AddArrayControlAction.TYPE;
}
}
AddArrayControlAction.TYPE = 'ngrx/forms/ADD_ARRAY_CONTROL';
class AddGroupControlAction {
constructor(controlId, name, value) {
this.controlId = controlId;
this.name = name;
this.value = value;
this.type = AddGroupControlAction.TYPE;
}
}
AddGroupControlAction.TYPE = 'ngrx/forms/ADD_GROUP_CONTROL';
class RemoveArrayControlAction {
constructor(controlId, index) {
this.controlId = controlId;
this.index = index;
this.type = RemoveArrayControlAction.TYPE;
}
}
RemoveArrayControlAction.TYPE = 'ngrx/forms/REMOVE_ARRAY_CONTROL';
class SwapArrayControlAction {
constructor(controlId, fromIndex, toIndex) {
this.controlId = controlId;
this.fromIndex = fromIndex;
this.toIndex = toIndex;
this.type = SwapArrayControlAction.TYPE;
}
}
SwapArrayControlAction.TYPE = 'ngrx/forms/SWAP_ARRAY_CONTROL';
class MoveArrayControlAction {
constructor(controlId, fromIndex, toIndex) {
this.controlId = controlId;
this.fromIndex = fromIndex;
this.toIndex = toIndex;
this.type = MoveArrayControlAction.TYPE;
}
}
MoveArrayControlAction.TYPE = 'ngrx/forms/MOVE_ARRAY_CONTROL';
class RemoveGroupControlAction {
constructor(controlId, name) {
this.controlId = controlId;
this.name = name;
this.type = RemoveGroupControlAction.TYPE;
}
}
RemoveGroupControlAction.TYPE = 'ngrx/forms/REMOVE_CONTROL';
class SetUserDefinedPropertyAction {
constructor(controlId, name, value) {
this.controlId = controlId;
this.name = name;
this.value = value;
this.type = SetUserDefinedPropertyAction.TYPE;
}
}
SetUserDefinedPropertyAction.TYPE = 'ngrx/forms/SET_USER_DEFINED_PROPERTY';
class ResetAction {
constructor(controlId) {
this.controlId = controlId;
this.type = ResetAction.TYPE;
}
}
ResetAction.TYPE = 'ngrx/forms/RESET';
function isNgrxFormsAction(action) {
return !!action.type && action.type.startsWith('ngrx/forms/');
}
const ALL_NGRX_FORMS_ACTION_TYPES = [
SetValueAction.TYPE,
SetErrorsAction.TYPE,
SetAsyncErrorAction.TYPE,
ClearAsyncErrorAction.TYPE,
StartAsyncValidationAction.TYPE,
MarkAsDirtyAction.TYPE,
MarkAsPristineAction.TYPE,
EnableAction.TYPE,
DisableAction.TYPE,
MarkAsTouchedAction.TYPE,
MarkAsUntouchedAction.TYPE,
FocusAction.TYPE,
UnfocusAction.TYPE,
MarkAsSubmittedAction.TYPE,
MarkAsUnsubmittedAction.TYPE,
AddGroupControlAction.TYPE,
RemoveGroupControlAction.TYPE,
AddArrayControlAction.TYPE,
RemoveArrayControlAction.TYPE,
SetUserDefinedPropertyAction.TYPE,
ResetAction.TYPE,
SwapArrayControlAction.TYPE,
MoveArrayControlAction.TYPE,
];
function isBoxed(value) {
return !!value && value.__boxed === '';
}
function box(value) {
return {
__boxed: '',
value,
};
}
function unbox(value) {
if (['string', 'boolean', 'number', 'undefined'].indexOf(typeof value) >= 0 || value === null || value === undefined) {
return value;
}
if (isBoxed(value)) {
return value.value;
}
if (Array.isArray(value)) {
return value.map(unbox);
}
return Object.keys(value).reduce((a, k) => Object.assign(a, { [k]: unbox(value[k]) }), {});
}
function isEmpty(obj) {
return Object.keys(obj).length === 0;
}
const defaultOptions = {
treatUndefinedAndMissingKeyAsSame: false,
};
function deepEquals(_1, _2, options = {}) {
const { treatUndefinedAndMissingKeyAsSame } = Object.assign({}, defaultOptions, options);
const leftChain = [];
const rightChain = [];
function compare2Objects(x, y) {
let p;
// remember that NaN === NaN returns false
// and isNaN(undefined) returns true
if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
return true;
}
// Compare primitives and functions.
// Check if both arguments link to the same object.
// Especially useful on the step where we compare prototypes
if (x === y) {
return true;
}
// Works in case when functions are created in constructor.
// Comparing dates is a common scenario. Another built-ins?
// We can even handle functions passed across iframes
if ((typeof x === 'function' && typeof y === 'function') ||
(x instanceof Date && y instanceof Date) ||
(x instanceof RegExp && y instanceof RegExp) ||
(x instanceof String && y instanceof String) ||
(x instanceof Number && y instanceof Number)) {
return x.toString() === y.toString();
}
// At last checking prototypes as good as we can
if (!(x instanceof Object && y instanceof Object)) {
return false;
}
if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
return false;
}
if (x.constructor !== y.constructor) {
return false;
}
// Check for infinitive linking loops
if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
return false;
}
// Quick checking of one object being a subset of another.
for (p in y) {
if (treatUndefinedAndMissingKeyAsSame && y.hasOwnProperty(p) && !x.hasOwnProperty(p) && y[p] === undefined) {
continue;
}
if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
return false;
}
else if (typeof y[p] !== typeof x[p]) {
return false;
}
}
// tslint:disable:forin
for (p in x) {
if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
if (!treatUndefinedAndMissingKeyAsSame || !x.hasOwnProperty(p) || y.hasOwnProperty(p) || x[p] !== undefined) {
return false;
}
}
switch (typeof (x[p])) {
case 'object':
case 'function':
leftChain.push(x);
rightChain.push(y);
if (!compare2Objects(x[p], y[p])) {
return false;
}
leftChain.pop();
rightChain.pop();
break;
default:
if (x[p] !== y[p]) {
return false;
}
break;
}
}
return true;
}
if (arguments.length <= 1) {
throw new Error('Need two or more arguments to compare');
}
return compare2Objects(_1, _2);
}
/**
* This function determines if a value is a form state.
*/
function isFormState(state) {
return !!state && state.hasOwnProperty('id') && state.hasOwnProperty('value') && state.hasOwnProperty('errors');
}
/**
* This function determines if a value is an array state.
*/
function isArrayState(state) {
return isFormState(state) && state.hasOwnProperty('controls') && Array.isArray(state.controls);
}
/**
* This function determines if a value is a group state.
*/
function isGroupState(state) {
return isFormState(state) && state.hasOwnProperty('controls') && !Array.isArray(state.controls) && typeof state.controls !== 'function';
}
function createChildState(id, childValue) {
if (isBoxed(childValue)) {
return createFormControlState(id, childValue);
}
if (childValue !== null && Array.isArray(childValue)) {
return createFormArrayState(id, childValue);
}
if (childValue !== null && typeof childValue === 'object') {
return createFormGroupState(id, childValue);
}
return createFormControlState(id, childValue);
}
function verifyFormControlValueIsValid(value) {
if (value === null || ['string', 'number', 'boolean', 'undefined'].indexOf(typeof value) >= 0) {
return value;
}
if (!isBoxed(value)) {
const errorMsg = 'Form control states only support undefined, null, string, number, and boolean values as well as boxed values';
throw new Error(`${errorMsg}; got ${JSON.stringify(value)} of type ${typeof value}`); // `;
}
if (value.value === null || ['string', 'number', 'boolean', 'undefined'].indexOf(typeof value.value) >= 0) {
return value;
}
const serialized = JSON.stringify(value);
const deserialized = JSON.parse(serialized);
if (deepEquals(value, deserialized, { treatUndefinedAndMissingKeyAsSame: true })) {
return value;
}
throw new Error(`A form control value must be serializable (i.e. value === JSON.parse(JSON.stringify(value))), got: ${JSON.stringify(value)}`);
}
/**
* This function creates a form control state with an ID and a value.
*/
function createFormControlState(id, value) {
return {
id,
value: verifyFormControlValueIsValid(value),
errors: {},
pendingValidations: [],
isValidationPending: false,
isValid: true,
isInvalid: false,
isEnabled: true,
isDisabled: false,
isDirty: false,
isPristine: true,
isTouched: false,
isUntouched: true,
isSubmitted: false,
isUnsubmitted: true,
isFocused: false,
isUnfocused: true,
userDefinedProperties: {},
};
}
function getFormGroupValue(controls, originalValue) {
let hasChanged = Object.keys(originalValue).length !== Object.keys(controls).length;
const newValue = Object.keys(controls).reduce((res, key) => {
const control = controls[key];
hasChanged = hasChanged || originalValue[key] !== control.value;
res[key] = control.value;
return res;
}, {});
return hasChanged ? newValue : originalValue;
}
function getFormGroupErrors(controls, originalErrors) {
let hasChanged = false;
const groupErrors = Object.keys(originalErrors)
.filter(key => !key.startsWith('_'))
.reduce((res, key) => Object.assign(res, { [key]: originalErrors[key] }), {});
const newErrors = Object.keys(controls).reduce((res, key) => {
const control = controls[key];
const controlErrors = control.errors;
if (!isEmpty(controlErrors)) {
hasChanged = hasChanged || originalErrors[`_${key}`] !== controlErrors;
Object.assign(res, { [`_${key}`]: control.errors });
}
else {
hasChanged = hasChanged || originalErrors.hasOwnProperty(`_${key}`);
}
return res;
}, groupErrors);
hasChanged = hasChanged || Object.keys(originalErrors).length !== Object.keys(newErrors).length;
return hasChanged ? newErrors : originalErrors;
}
function computeGroupState(id, controls, value, errors, pendingValidations, userDefinedProperties, flags) {
value = getFormGroupValue(controls, value);
errors = getFormGroupErrors(controls, errors);
const isValid = isEmpty(errors);
const isDirty = flags.wasOrShouldBeDirty || Object.keys(controls).some(key => controls[key].isDirty);
const isEnabled = flags.wasOrShouldBeEnabled || Object.keys(controls).some(key => controls[key].isEnabled);
const isTouched = flags.wasOrShouldBeTouched || Object.keys(controls).some(key => controls[key].isTouched);
const isSubmitted = flags.wasOrShouldBeSubmitted || Object.keys(controls).some(key => controls[key].isSubmitted);
const isValidationPending = pendingValidations.length > 0 || Object.keys(controls).some(key => controls[key].isValidationPending);
return {
id,
value,
errors,
pendingValidations,
isValidationPending,
isValid,
isInvalid: !isValid,
isEnabled,
isDisabled: !isEnabled,
isDirty,
isPristine: !isDirty,
isTouched,
isUntouched: !isTouched,
isSubmitted,
isUnsubmitted: !isSubmitted,
userDefinedProperties,
controls,
};
}
/**
* This function creates a form group state with an ID and a value.
* From the value the shape of the group state is inferred, i.e.
* object properties are inferred as form groups, array properties
* are inferred as form arrays, and primitive properties are inferred
* as form controls.
*/
function createFormGroupState(id, initialValue) {
const controls = Object.keys(initialValue)
.map((key) => [key, createChildState(`${id}.${key}`, initialValue[key])])
.reduce((res, [controlId, state]) => Object.assign(res, { [controlId]: state }), {});
return computeGroupState(id, controls, initialValue, {}, [], {}, { wasOrShouldBeEnabled: true });
}
function getFormArrayValue(controls, originalValue) {
let hasChanged = Object.keys(originalValue).length !== Object.keys(controls).length;
const newValue = controls.map((state, i) => {
hasChanged = hasChanged || originalValue[i] !== state.value;
return state.value;
});
return hasChanged ? newValue : originalValue;
}
function getFormArrayErrors(controls, originalErrors) {
let hasChanged = false;
const groupErrors = Object.keys(originalErrors)
.filter(key => !key.startsWith('_'))
.reduce((res, key) => Object.assign(res, { [key]: originalErrors[key] }), {});
const newErrors = controls.reduce((res, state, i) => {
const controlErrors = state.errors;
if (!isEmpty(controlErrors)) {
hasChanged = hasChanged || originalErrors[`_${i}`] !== controlErrors;
Object.assign(res, { [`_${i}`]: controlErrors });
}
else {
hasChanged = hasChanged || originalErrors.hasOwnProperty(`_${i}`);
}
return res;
}, groupErrors);
hasChanged = hasChanged || Object.keys(originalErrors).length !== Object.keys(newErrors).length;
return hasChanged ? newErrors : originalErrors;
}
function computeArrayState(id, inferredControls, value, errors, pendingValidations, userDefinedProperties, flags) {
const controls = inferredControls;
value = getFormArrayValue(controls, value);
errors = getFormArrayErrors(controls, errors);
const isValid = isEmpty(errors);
const isDirty = flags.wasOrShouldBeDirty || controls.some(state => state.isDirty);
const isEnabled = flags.wasOrShouldBeEnabled || controls.some(state => state.isEnabled);
const isTouched = flags.wasOrShouldBeTouched || controls.some(state => state.isTouched);
const isSubmitted = flags.wasOrShouldBeSubmitted || controls.some(state => state.isSubmitted);
const isValidationPending = pendingValidations.length > 0 || controls.some(state => state.isValidationPending);
return {
id,
value,
errors,
pendingValidations,
isValidationPending,
isValid,
isInvalid: !isValid,
isEnabled,
isDisabled: !isEnabled,
isDirty,
isPristine: !isDirty,
isTouched,
isUntouched: !isTouched,
isSubmitted,
isUnsubmitted: !isSubmitted,
userDefinedProperties,
controls: inferredControls,
};
}
/**
* This function creates a form array state with an ID and a value.
* From the value the shape of the array state is inferred, i.e.
* object values are inferred as form groups, array values
* are inferred as form arrays, and primitive values are inferred
* as form controls.
*/
function createFormArrayState(id, initialValue) {
const controls = initialValue
.map((value, i) => createChildState(`${id}.${i}`, value));
return computeArrayState(id, controls, initialValue, {}, [], {}, { wasOrShouldBeEnabled: true });
}
function clearAsyncErrorReducer$2(state, action) {
if (action.type !== ClearAsyncErrorAction.TYPE) {
return state;
}
const name = `$${action.name}`;
let errors = state.errors;
if (errors.hasOwnProperty(name)) {
errors = Object.assign({}, state.errors);
delete errors[name];
}
const pendingValidations = state.pendingValidations.filter(v => v !== action.name);
const isValid = isEmpty(errors);
if (errors === state.errors && isValid === state.isValid && pendingValidations.length === state.pendingValidations.length) {
return state;
}
return Object.assign(Object.assign({}, state), { isValid, isInvalid: !isValid, errors,
pendingValidations, isValidationPending: pendingValidations.length > 0 });
}
function disableReducer$2(state, action) {
if (action.type !== DisableAction.TYPE) {
return state;
}
if (state.isDisabled) {
return state;
}
return Object.assign(Object.assign({}, state), { isEnabled: false, isDisabled: true, isValid: true, isInvalid: false, errors: {}, pendingValidations: [], isValidationPending: false });
}
function enableReducer$2(state, action) {
if (action.type !== EnableAction.TYPE) {
return state;
}
if (state.isEnabled) {
return state;
}
return Object.assign(Object.assign({}, state), { isEnabled: true, isDisabled: false });
}
function focusReducer(state, action) {
if (action.type !== FocusAction.TYPE) {
return state;
}
if (state.isFocused) {
return state;
}
return Object.assign(Object.assign({}, state), { isFocused: true, isUnfocused: false });
}
function markAsDirtyReducer$2(state, action) {
if (action.type !== MarkAsDirtyAction.TYPE) {
return state;
}
if (state.isDirty) {
return state;
}
return Object.assign(Object.assign({}, state), { isDirty: true, isPristine: false });
}
function markAsPristineReducer$2(state, action) {
if (action.type !== MarkAsPristineAction.TYPE) {
return state;
}
if (state.isPristine) {
return state;
}
return Object.assign(Object.assign({}, state), { isDirty: false, isPristine: true });
}
function markAsSubmittedReducer$2(state, action) {
if (action.type !== MarkAsSubmittedAction.TYPE) {
return state;
}
if (state.isSubmitted) {
return state;
}
return Object.assign(Object.assign({}, state), { isSubmitted: true, isUnsubmitted: false });
}
function markAsTouchedReducer$2(state, action) {
if (action.type !== MarkAsTouchedAction.TYPE) {
return state;
}
if (state.isTouched) {
return state;
}
return Object.assign(Object.assign({}, state), { isTouched: true, isUntouched: false });
}
function markAsUnsubmittedReducer$2(state, action) {
if (action.type !== MarkAsUnsubmittedAction.TYPE) {
return state;
}
if (state.isUnsubmitted) {
return state;
}
return Object.assign(Object.assign({}, state), { isSubmitted: false, isUnsubmitted: true });
}
function markAsUntouchedReducer$2(state, action) {
if (action.type !== MarkAsUntouchedAction.TYPE) {
return state;
}
if (state.isUntouched) {
return state;
}
return Object.assign(Object.assign({}, state), { isTouched: false, isUntouched: true });
}
function resetReducer$2(state, action) {
if (action.type !== ResetAction.TYPE) {
return state;
}
if (state.isPristine && state.isUntouched && state.isUnsubmitted) {
return state;
}
return Object.assign(Object.assign({}, state), { isDirty: false, isPristine: true, isTouched: false, isUntouched: true, isSubmitted: false, isUnsubmitted: true });
}
function setAsyncErrorReducer$2(state, action) {
if (action.type !== SetAsyncErrorAction.TYPE) {
return state;
}
if (state.isDisabled) {
return state;
}
const name = `$${action.name}`;
let value = action.value;
if (deepEquals(state.errors[name], action.value)) {
value = state.errors[name];
}
const errors = Object.assign(Object.assign({}, state.errors), { [name]: value });
const pendingValidations = state.pendingValidations.filter(v => v !== action.name);
return Object.assign(Object.assign({}, state), { isValid: false, isInvalid: true, errors,
pendingValidations, isValidationPending: pendingValidations.length > 0 });
}
function setErrorsReducer$2(state, action) {
if (action.type !== SetErrorsAction.TYPE) {
return state;
}
if (state.isDisabled) {
return state;
}
if (state.errors === action.errors) {
return state;
}
if (deepEquals(state.errors, action.errors)) {
return state;
}
if (!action.errors || typeof action.errors !== 'object' || Array.isArray(action.errors)) {
throw new Error(`Control errors must be an object; got ${action.errors}`); // `;
}
if (Object.keys(action.errors).some(key => key.startsWith('$'))) {
throw new Error(`Control errors must not use $ as a prefix; got ${JSON.stringify(action.errors)}`); // `;
}
const asyncErrors = Object.keys(state.errors)
.filter(key => key.startsWith('$'))
.reduce((res, key) => Object.assign(res, { [key]: state.errors[key] }), {});
const newErrors = isEmpty(asyncErrors) ? action.errors : Object.assign(asyncErrors, action.errors);
const isValid = isEmpty(newErrors);
return Object.assign(Object.assign({}, state), { isValid, isInvalid: !isValid, errors: newErrors });
}
function setUserDefinedPropertyReducer$2(state, action) {
if (action.type !== SetUserDefinedPropertyAction.TYPE) {
return state;
}
if (state.userDefinedProperties[action.name] === action.value) {
return state;
}
return Object.assign(Object.assign({}, state), { userDefinedProperties: Object.assign(Object.assign({}, state.userDefinedProperties), { [action.name]: action.value }) });
}
function setValueReducer$2(state, action) {
if (action.type !== SetValueAction.TYPE) {
return state;
}
if (state.value === action.value) {
return state;
}
return Object.assign(Object.assign({}, state), { value: verifyFormControlValueIsValid(action.value) });
}
function startAsyncValidationReducer$2(state, action) {
if (action.type !== StartAsyncValidationAction.TYPE) {
return state;
}
if (state.pendingValidations.indexOf(action.name) >= 0) {
return state;
}
return Object.assign(Object.assign({}, state), { pendingValidations: [...state.pendingValidations, action.name], isValidationPending: true });
}
function unfocusReducer(state, action) {
if (action.type !== UnfocusAction.TYPE) {
return state;
}
if (state.isUnfocused) {
return state;
}
return Object.assign(Object.assign({}, state), { isFocused: false, isUnfocused: true });
}
function formControlReducerInternal(state, action) {
if (isGroupState(state) || isArrayState(state)) {
throw new Error('The state must be a control state');
}
if (action.controlId !== state.id) {
return state;
}
state = setValueReducer$2(state, action);
state = setErrorsReducer$2(state, action);
state = startAsyncValidationReducer$2(state, action);
state = setAsyncErrorReducer$2(state, action);
state = clearAsyncErrorReducer$2(state, action);
state = enableReducer$2(state, action);
state = disableReducer$2(state, action);
state = focusReducer(state, action);
state = unfocusReducer(state, action);
state = markAsDirtyReducer$2(state, action);
state = markAsPristineReducer$2(state, action);
state = markAsTouchedReducer$2(state, action);
state = markAsUntouchedReducer$2(state, action);
state = markAsSubmittedReducer$2(state, action);
state = markAsUnsubmittedReducer$2(state, action);
state = setUserDefinedPropertyReducer$2(state, action);
state = resetReducer$2(state, action);
return state;
}
/**
* This reducer function updates a form control state with actions.
*/
function formControlReducer(state, action) {
if (!state) {
throw new Error('The control state must be defined!');
}
return formControlReducerInternal(state, action);
}
function dispatchActionPerChild$1(controls, actionCreator) {
let hasChanged = false;
const newControls = controls
.map(state => {
const newState = formStateReducer(state, actionCreator(state.id));
hasChanged = hasChanged || state !== newState;
return newState;
});
return hasChanged ? newControls : controls;
}
function callChildReducers$1(controls, action) {
let hasChanged = false;
const newControls = controls
.map(state => {
const newState = formStateReducer(state, action);
hasChanged = hasChanged || state !== newState;
return newState;
});
return hasChanged ? newControls : controls;
}
function childReducer$1(state, action) {
const controls = callChildReducers$1(state.controls, action);
if (state.controls === controls) {
return state;
}
return computeArrayState(state.id, controls, state.value, state.errors, state.pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: state.isDirty,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: state.isTouched,
wasOrShouldBeSubmitted: state.isSubmitted,
});
}
function updateIdRecursiveForGroup(state, newId) {
const controls = Object.keys(state.controls)
.reduce((agg, key) => Object.assign(agg, {
[key]: updateIdRecursive(state.controls[key], `${newId}.${key}`),
}), {});
return Object.assign(Object.assign({}, state), { id: newId, controls });
}
function updateIdRecursiveForArray(state, newId) {
const controls = state.controls.map((c, i) => updateIdRecursive(c, `${newId}.${i}`));
return Object.assign(Object.assign({}, state), { id: newId, controls });
}
function updateIdRecursive(state, newId) {
if (state.id === newId) {
return state;
}
if (isGroupState(state)) {
return updateIdRecursiveForGroup(state, newId);
}
if (isArrayState(state)) {
return updateIdRecursiveForArray(state, newId);
}
return Object.assign(Object.assign({}, state), { id: newId });
}
function addControlReducer$1(state, action) {
if (action.type !== AddArrayControlAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
const index = action.index === undefined ? state.controls.length : action.index;
if (index > state.controls.length || index < 0) {
throw new Error(`Index ${index} is out of bounds for array '${state.id}' with length ${state.controls.length}!`);
}
let controls = [...state.controls];
controls.splice(index, 0, createChildState(`${state.id}.${index}`, action.value));
controls = controls.map((c, i) => updateIdRecursive(c, `${state.id}.${i}`));
return computeArrayState(state.id, controls, state.value, state.errors, state.pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: true,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: state.isTouched,
wasOrShouldBeSubmitted: state.isSubmitted,
});
}
function clearAsyncErrorReducer$1(state, action) {
if (action.type !== ClearAsyncErrorAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
const name = `$${action.name}`;
let errors = state.errors;
if (state.errors.hasOwnProperty(name)) {
errors = Object.assign({}, state.errors);
delete errors[name];
}
const pendingValidations = state.pendingValidations.filter(v => v !== action.name);
if (errors === state.errors && pendingValidations.length === state.pendingValidations.length) {
return state;
}
return computeArrayState(state.id, state.controls, state.value, errors, pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: state.isDirty,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: state.isTouched,
wasOrShouldBeSubmitted: state.isSubmitted,
});
}
function disableReducer$1(state, action) {
if (action.type !== DisableAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
if (state.isDisabled) {
return state;
}
return computeArrayState(state.id, dispatchActionPerChild$1(state.controls, controlId => new DisableAction(controlId)), state.value, {}, [], state.userDefinedProperties, {
wasOrShouldBeDirty: state.isDirty,
wasOrShouldBeEnabled: false,
wasOrShouldBeTouched: state.isTouched,
wasOrShouldBeSubmitted: state.isSubmitted,
});
}
function enableReducer$1(state, action) {
if (action.type !== EnableAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
const controls = dispatchActionPerChild$1(state.controls, controlId => new EnableAction(controlId));
if (controls === state.controls && state.isEnabled) {
return state;
}
return computeArrayState(state.id, controls, state.value, state.errors, state.pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: state.isDirty,
wasOrShouldBeEnabled: true,
wasOrShouldBeTouched: state.isTouched,
wasOrShouldBeSubmitted: state.isSubmitted,
});
}
function markAsDirtyReducer$1(state, action) {
if (action.type !== MarkAsDirtyAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
const controls = dispatchActionPerChild$1(state.controls, controlId => new MarkAsDirtyAction(controlId));
if (controls === state.controls && state.isDirty) {
return state;
}
return computeArrayState(state.id, controls, state.value, state.errors, state.pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: true,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: state.isTouched,
wasOrShouldBeSubmitted: state.isSubmitted,
});
}
function markAsPristineReducer$1(state, action) {
if (action.type !== MarkAsPristineAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
if (state.isPristine) {
return state;
}
return computeArrayState(state.id, dispatchActionPerChild$1(state.controls, controlId => new MarkAsPristineAction(controlId)), state.value, state.errors, state.pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: false,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: state.isTouched,
wasOrShouldBeSubmitted: state.isSubmitted,
});
}
function markAsSubmittedReducer$1(state, action) {
if (action.type !== MarkAsSubmittedAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
const controls = dispatchActionPerChild$1(state.controls, controlId => new MarkAsSubmittedAction(controlId));
if (controls === state.controls) {
return state;
}
return computeArrayState(state.id, controls, state.value, state.errors, state.pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: state.isDirty,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: state.isTouched,
wasOrShouldBeSubmitted: true,
});
}
function markAsTouchedReducer$1(state, action) {
if (action.type !== MarkAsTouchedAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
const controls = dispatchActionPerChild$1(state.controls, controlId => new MarkAsTouchedAction(controlId));
if (controls === state.controls) {
return state;
}
return computeArrayState(state.id, controls, state.value, state.errors, state.pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: state.isDirty,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: true,
wasOrShouldBeSubmitted: state.isSubmitted,
});
}
function markAsUnsubmittedReducer$1(state, action) {
if (action.type !== MarkAsUnsubmittedAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
if (state.isUnsubmitted) {
return state;
}
return computeArrayState(state.id, dispatchActionPerChild$1(state.controls, controlId => new MarkAsUnsubmittedAction(controlId)), state.value, state.errors, state.pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: state.isDirty,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: state.isTouched,
wasOrShouldBeSubmitted: false,
});
}
function markAsUntouchedReducer$1(state, action) {
if (action.type !== MarkAsUntouchedAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
if (state.isUntouched) {
return state;
}
return computeArrayState(state.id, dispatchActionPerChild$1(state.controls, controlId => new MarkAsUntouchedAction(controlId)), state.value, state.errors, state.pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: state.isDirty,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: false,
wasOrShouldBeSubmitted: state.isSubmitted,
});
}
function move(array, fromIndex, toIndex) {
const item = array[fromIndex];
const length = array.length;
if (fromIndex > toIndex) {
return [
...array.slice(0, toIndex),
item,
...array.slice(toIndex, fromIndex),
...array.slice(fromIndex + 1, length),
];
}
else {
const targetIndex = toIndex + 1;
return [
...array.slice(0, fromIndex),
...array.slice(fromIndex + 1, targetIndex),
item,
...array.slice(targetIndex, length),
];
}
}
function moveControlReducer(state, action) {
if (action.type !== MoveArrayControlAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
const fromIndex = action.fromIndex;
const toIndex = action.toIndex;
if (fromIndex === toIndex) {
return state;
}
if (fromIndex < 0 || toIndex < 0) {
throw new Error(`fromIndex ${fromIndex} or toIndex ${fromIndex} was negative`);
}
if (fromIndex >= state.controls.length || toIndex >= state.controls.length) {
throw new Error(`fromIndex ${fromIndex} or toIndex ${toIndex} is out of bounds with the length of the controls ${state.controls.length}`);
}
let controls = move(state.controls, fromIndex, toIndex);
controls = controls.map((c, i) => updateIdRecursive(c, `${state.id}.${i}`));
return computeArrayState(state.id, controls, state.value, state.errors, state.pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: true,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: state.isTouched,
wasOrShouldBeSubmitted: state.isSubmitted,
});
}
function removeControlReducer$1(state, action) {
if (action.type !== RemoveArrayControlAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
if (action.index >= state.controls.length || action.index < 0) {
throw new Error(`Index ${action.index} is out of bounds for array '${state.id}' with length ${state.controls.length}!`);
}
const index = action.index;
const controls = state.controls.filter((_, i) => i !== index).map((c, i) => updateIdRecursive(c, `${state.id}.${i}`));
return computeArrayState(state.id, controls, state.value, state.errors, state.pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: true,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: state.isTouched,
wasOrShouldBeSubmitted: state.isSubmitted,
});
}
function resetReducer$1(state, action) {
if (action.type !== ResetAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
if (state.isPristine && state.isUntouched && state.isUnsubmitted) {
return state;
}
return computeArrayState(state.id, dispatchActionPerChild$1(state.controls, controlId => new ResetAction(controlId)), state.value, state.errors, state.pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: false,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: false,
wasOrShouldBeSubmitted: false,
});
}
function setAsyncErrorReducer$1(state, action) {
if (action.type !== SetAsyncErrorAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
if (state.isDisabled) {
return state;
}
const name = `$${action.name}`;
let value = action.value;
if (deepEquals(state.errors[name], action.value)) {
value = state.errors[name];
}
const errors = Object.assign(Object.assign({}, state.errors), { [name]: value });
const pendingValidations = state.pendingValidations.filter(v => v !== action.name);
return computeArrayState(state.id, state.controls, state.value, errors, pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: state.isDirty,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: state.isTouched,
wasOrShouldBeSubmitted: state.isSubmitted,
});
}
function setErrorsReducer$1(state, action) {
if (action.type !== SetErrorsAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
if (state.isDisabled) {
return state;
}
if (state.errors === action.errors) {
return state;
}
if (deepEquals(state.errors, action.errors)) {
return state;
}
if (!action.errors || typeof action.errors !== 'object' || Array.isArray(action.errors)) {
throw new Error(`Control errors must be an object; got ${action.errors}`);
}
if (Object.keys(action.errors).some(key => key.startsWith('_'))) {
throw new Error(`Control errors must not use underscore as a prefix; got ${JSON.stringify(action.errors)}`);
}
if (Object.keys(action.errors).some(key => key.startsWith('$'))) {
throw new Error(`Control errors must not use $ as a prefix; got ${JSON.stringify(action.errors)}`);
}
const childAndAsyncErrors = Object.keys(state.errors)
.filter(key => key.startsWith('_') || key.startsWith('$'))
.reduce((res, key) => Object.assign(res, { [key]: state.errors[key] }), {});
const newErrors = Object.assign(childAndAsyncErrors, action.errors);
return computeArrayState(state.id, state.controls, state.value, newErrors, state.pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: state.isDirty,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: state.isTouched,
wasOrShouldBeSubmitted: state.isSubmitted,
});
}
function setUserDefinedPropertyReducer$1(state, action) {
if (action.type !== SetUserDefinedPropertyAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
if (state.userDefinedProperties[action.name] === action.value) {
return state;
}
return Object.assign(Object.assign({}, state), { userDefinedProperties: Object.assign(Object.assign({}, state.userDefinedProperties), { [action.name]: action.value }) });
}
function setValueReducer$1(state, action) {
if (action.type !== SetValueAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
if (state.value === action.value) {
return state;
}
if (action.value instanceof Date) {
throw new Error('Date values are not supported. Please used serialized strings instead.');
}
const value = action.value;
const controls = value
.map((v, i) => {
if (!state.controls[i]) {
return createChildState(`${state.id}.${i}`, v);
}
return formStateReducer(state.controls[i], new SetValueAction(state.controls[i].id, v));
});
return computeArrayState(state.id, controls, value, state.errors, state.pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: state.isDirty,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: state.isTouched,
wasOrShouldBeSubmitted: state.isSubmitted,
});
}
function startAsyncValidationReducer$1(state, action) {
if (action.type !== StartAsyncValidationAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
if (state.pendingValidations.indexOf(action.name) >= 0) {
return state;
}
const pendingValidations = [...state.pendingValidations, action.name];
return computeArrayState(state.id, state.controls, state.value, state.errors, pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: state.isDirty,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: state.isTouched,
wasOrShouldBeSubmitted: state.isSubmitted,
});
}
function swapArrayValues(a, i, j) {
const n = [...a];
[n[i], n[j]] = [n[j], n[i]];
return n;
}
function swapControlReducer(state, action) {
if (action.type !== SwapArrayControlAction.TYPE) {
return state;
}
if (action.controlId !== state.id) {
return childReducer$1(state, action);
}
const fromIndex = action.fromIndex;
const toIndex = action.toIndex;
if (fromIndex === toIndex) {
return state;
}
if (fromIndex < 0 || toIndex < 0) {
throw new Error(`fromIndex ${fromIndex} or toIndex ${fromIndex} was negative`);
}
if (fromIndex >= state.controls.length || toIndex >= state.controls.length) {
throw new Error(`fromIndex ${fromIndex} or toIndex ${toIndex} is out of bounds with the length of the controls ${state.controls.length}`);
}
let controls = swapArrayValues(state.controls, fromIndex, toIndex);
controls = controls.map((c, i) => (i >= fromIndex || i >= toIndex) ? updateIdRecursive(c, `${state.id}.${i}`) : c);
return computeArrayState(state.id, controls, state.value, state.errors, state.pendingValidations, state.userDefinedProperties, {
wasOrShouldBeDirty: true,
wasOrShouldBeEnabled: state.isEnabled,
wasOrShouldBeTouched: state.isTouched,
wasOrShouldBeSubmitted: state.isSubmitted,
});
}
function formArrayReducerInternal(state, action) {
if (!isArrayState(state)) {
throw new Error('The state must be an array state');
}
if (!isNgrxFormsAction(action)) {
return state;
}
if (!action.controlId.startsWith(state.id)) {
return state;
}
switch (action.type) {
case FocusAction.TYPE:
case UnfocusAction.TYPE:
case AddGroupControlAction.TYPE:
case RemoveGroupControlAction.TYPE:
return childReducer$1(state, action);
default:
break;
}
state = setValueReducer$1(state, action);
state = setErrorsReducer$1(state, action);
state = startAsyncValidationReducer$1(state, action);
state = setAsyncErrorReducer$1(state, action);
state = clearAsyncErrorReducer$1(state, action);
state = enableReducer$1(state, action);
state = disableReducer$1(state, action);
state = markAsDirtyReducer$1(state, action);
state = markAsPristineReducer$1(state, action);
state = markAsTouchedReducer$1(state, action);
state = markAsUntouchedReducer$1(state, action);
state = markAsSubmittedReducer$1(state, action);
state = markAsUnsubmittedReducer$1(state, action);
state = setUserDefinedPropertyReducer$1(state, action);
state = resetReducer$1(state, action);
state = addControlReducer$1(state, action);
state = removeControlReducer$1(state, action);
state = swapControlReducer(state, action);
state = moveControlReducer(state, action);
return state;
}
/