mobx-react-form
Version:
Reactive MobX Form State Management
848 lines (845 loc) • 32.9 kB
JavaScript
import { makeObservable, action, computed, observable, toJS, observe, intercept } from 'mobx';
import { ArrayMap } from './ArrayMap.js';
import { forIn, get, merge, transform, isPlainObject, each, has, set } from 'lodash-es';
import { $try, hasIntKeys, isEvent, pathToStruct, allowedProps, checkPropOccurrence, getObservableMapValues, throwError, isArrayOfObjects, maxKey } from './utils.js';
import { prepareFieldsData, mergeSchemaDefaults, parseInput, pathToFieldsTree, parseCheckOutput, parseCheckArray, defaultValue, parsePath } from './parser.js';
import { FieldPropsEnum, SeparatedPropsMode, AllowedFieldPropsTypes } from './models/FieldProps.js';
import { OptionsEnum } from './models/OptionsModel.js';
import { ValidationHooks } from './models/ValidatorInterface.js';
import { props } from './props.js';
class Base {
noop = () => { };
state;
fields = new ArrayMap();
path;
$submitted = 0;
$submitting = false;
$validated = 0;
$validating = false;
$clearing = false;
$resetting = false;
$touched = false;
$changed = 0;
$hooks = {};
$handlers = {};
constructor() {
makeObservable(this, {
$submitted: observable,
$submitting: observable,
$validated: observable,
$validating: observable,
$clearing: observable,
$resetting: observable,
$touched: observable,
$changed: observable,
$hooks: observable,
$handlers: observable,
changed: computed,
submitted: computed,
submitting: computed,
validated: computed,
validating: computed,
clearing: computed,
resetting: computed,
hasIncrementalKeys: computed,
hasNestedFields: computed,
size: computed,
// initialization
initField: action,
// actions
submit: action,
deepUpdate: action,
set: action,
add: action,
del: action,
});
}
execHook = (name, fallback = {}) => $try(fallback[name], this.$hooks[name], this.noop).apply(this, [this]);
execHandler = (name, args, fallback = undefined, hook = null, execHook = true) => [
$try(this.$handlers[name] && this.$handlers[name].apply(this, [this]), fallback, this.noop).apply(this, [...args]),
execHook && this.execHook(hook || name),
];
get resetting() {
return this.hasNestedFields
? this.check(FieldPropsEnum.resetting, true)
: this.$resetting;
}
get clearing() {
return this.hasNestedFields
? this.check(FieldPropsEnum.clearing, true)
: this.$clearing;
}
get submitted() {
return toJS(this.$submitted);
}
get submitting() {
return toJS(this.$submitting);
}
get validated() {
return toJS(this.$validated);
}
get validating() {
return toJS(this.$validating);
}
get hasIncrementalKeys() {
return !!this.fields.size && hasIntKeys(this.fields);
}
get hasNestedFields() {
return this.fields.size !== 0;
}
get size() {
return this.fields.size;
}
get changed() {
return this.path != null && this.hasNestedFields
? this.reduce((acc, field) => acc + field.changed, 0) + this.$changed
: this.$changed;
}
/**
Interceptor
*/
intercept = (opt) => this.MOBXEvent(typeof opt === "function"
? { type: "interceptor", call: opt }
: { type: "interceptor", ...opt });
/**
Observer
*/
observe = (opt) => this.MOBXEvent(typeof opt === "function"
? { type: "observer", call: opt }
: { type: "observer", ...opt });
/**
Event Handler: On Clear
*/
onClear = (...args) => this.execHandler(FieldPropsEnum.onClear, args, (e) => {
isEvent(e) && e.preventDefault();
this.clear(true, false);
});
/**
Event Handler: On Reset
*/
onReset = (...args) => this.execHandler(FieldPropsEnum.onReset, args, (e) => {
isEvent(e) && e.preventDefault();
this.reset(true, false);
});
/**
Event Handler: On Submit
*/
onSubmit = (...args) => this.execHandler(FieldPropsEnum.onSubmit, args, (e, o = {}) => {
isEvent(e) && e.preventDefault();
this.submit(o);
}, null, false);
/**
Event Handler: On Add
*/
onAdd = (...args) => this.execHandler(FieldPropsEnum.onAdd, args, (e, val) => {
isEvent(e) && e.preventDefault();
this.add(isEvent(val) ? null : val, false);
});
/**
Event Handler: On Del
*/
onDel = (...args) => this.execHandler(FieldPropsEnum.onDel, args, (e, path) => {
isEvent(e) && e.preventDefault();
this.del(isEvent(path) ? this.path : path, false);
});
/******************************************************************
Initializer
*/
initFields(initial, update = false) {
const fallback = this.state.options.get(OptionsEnum.fallback);
const $path = (key) => [this.path, key].join(".").replace(/^\.+/, "");
let fields;
fields = prepareFieldsData(initial, this.state.strict, fallback);
fields = mergeSchemaDefaults(fields, this.validator);
// create fields
forIn(fields, (field, key) => {
const path = $path(key);
const $f = this.select(path, null, false);
if ($f == null) {
if (fallback) {
this.initField(key, path, field, update);
}
else {
const structPath = pathToStruct(path);
const struct = this.state.struct();
const found = struct
.filter((s) => s.startsWith(structPath))
.find((s) => s.charAt(structPath.length) === "." ||
s.substring(structPath.length, structPath.length + 2) ===
"[]" ||
s === structPath);
if (found)
this.initField(key, path, field, update);
}
}
});
}
initField(key, path, data, update = false) {
const initial = this.state.get("current", "props");
const struct = pathToStruct(path);
// try to get props from separated objects
const _try = (prop) => {
const t = get(initial[prop], struct);
if ([
FieldPropsEnum.input,
FieldPropsEnum.output,
FieldPropsEnum.converter,
].includes(prop) &&
typeof t !== "function")
return undefined;
return t;
};
const props = {
$value: get(initial[SeparatedPropsMode.values], path),
$computed: _try(SeparatedPropsMode.computed),
$label: _try(SeparatedPropsMode.labels),
$placeholder: _try(SeparatedPropsMode.placeholders),
$default: _try(SeparatedPropsMode.defaults),
$initial: _try(SeparatedPropsMode.initials),
$disabled: _try(SeparatedPropsMode.disabled),
$deleted: _try(SeparatedPropsMode.deleted),
$type: _try(SeparatedPropsMode.types),
$related: _try(SeparatedPropsMode.related),
$rules: _try(SeparatedPropsMode.rules),
$options: _try(SeparatedPropsMode.options),
$bindings: _try(SeparatedPropsMode.bindings),
$extra: _try(SeparatedPropsMode.extra),
$hooks: _try(SeparatedPropsMode.hooks),
$handlers: _try(SeparatedPropsMode.handlers),
$validatedWith: _try(SeparatedPropsMode.validatedWith),
$validators: _try(SeparatedPropsMode.validators),
$observers: _try(SeparatedPropsMode.observers),
$interceptors: _try(SeparatedPropsMode.interceptors),
$converters: _try(SeparatedPropsMode.converters),
$input: _try(SeparatedPropsMode.input),
$output: _try(SeparatedPropsMode.output),
$autoFocus: _try(SeparatedPropsMode.autoFocus),
$ref: _try(SeparatedPropsMode.refs),
$nullable: _try(SeparatedPropsMode.nullable),
$autoComplete: _try(SeparatedPropsMode.autoComplete),
};
const field = this.state.form.makeField({
key,
path,
struct,
data,
props,
update,
state: this.state,
}, (data && data[FieldPropsEnum.class]) || _try(SeparatedPropsMode.classes));
this.fields.merge({ [key]: field });
return field;
}
/******************************************************************
Actions
*/
validate(opt, obj) {
const $opt = merge(opt, { path: this.path });
return this.state.form.validator.validate($opt, obj);
}
/**
Submit
*/
submit(hooks = {}, { execOnSubmitHook = true, execValidationHooks = true, validate = true, } = {}) {
const execOnSubmit = () => this.execHook(FieldPropsEnum.onSubmit, hooks);
const submit = execOnSubmitHook ? execOnSubmit() : undefined;
this.$submitting = true;
this.$submitted += 1;
if (!validate ||
!this.state.options.get(OptionsEnum.validateOnSubmit, this)) {
return Promise.resolve(submit)
.then(action(() => (this.$submitting = false)))
.catch(action((err) => {
this.$submitting = false;
throw err;
}))
.then(() => this);
}
const exec = (isValid) => isValid
? this.execHook(ValidationHooks.onSuccess, hooks)
: this.execHook(ValidationHooks.onError, hooks);
return this.validate({
showErrors: this.state.options.get(OptionsEnum.showErrorsOnSubmit, this),
})
.then(({ isValid }) => {
const handler = execValidationHooks ? exec(isValid) : undefined;
if (isValid)
return Promise.all([submit, handler]);
const $err = this.state.options.get(OptionsEnum.defaultGenericError, this);
const $throw = this.state.options.get(OptionsEnum.submitThrowsError, this);
if ($throw && $err)
this.invalidate();
return Promise.all([submit, handler]);
})
.then(action(() => (this.$submitting = false)))
.catch(action((err) => {
this.$submitting = false;
throw err;
}))
.then(() => this);
}
/**
Check Field Computed Values
*/
check(prop, deep = false) {
allowedProps(AllowedFieldPropsTypes.computed, [prop]);
return deep
? checkPropOccurrence({
type: props.occurrences[prop],
data: this.deepCheck(props.occurrences[prop], prop, this.fields),
})
: this[prop];
}
deepCheck(type, prop, fields) {
const $fields = getObservableMapValues(fields);
return transform($fields, (check, field) => {
if (!field.fields.size || !Array.isArray(field.initial)) {
check.push(field[prop]);
}
check.push(checkPropOccurrence({
data: this.deepCheck(type, prop, field.fields),
type,
}));
return check;
}, []);
}
firstError() {
if (!this.state.options.get(OptionsEnum.bubbleUpErrorMessages, this))
return null;
for (const field of getObservableMapValues(this.fields)) {
if (field.error)
return field.error;
if (field.fields.size) {
const nested = field.firstError();
if (nested)
return nested;
}
}
return null;
}
/**
Update Field Values recurisvely
OR Create Field if 'undefined'
*/
update(fields) {
if (!isPlainObject(fields)) {
throw new Error("The update() method accepts only plain objects.");
}
this.deepUpdate(prepareFieldsData({ fields }, this.state.strict), undefined, undefined, fields);
}
deepUpdate(fields, path = "", recursion = true, raw) {
each(fields, (field, key) => {
const $key = has(field, FieldPropsEnum.name) ? field.name : key;
const $path = `${path}.${$key}`.replace(/^\.+/, "");
const strictUpdate = this.state.options.get(OptionsEnum.strictUpdate, this);
const $field = this.select($path, null, strictUpdate);
const $container = this.select(path, null, false) ||
this.state.form.select(this.path ?? '', null, false);
const applyInputConverterOnUpdate = this.state.options.get(OptionsEnum.applyInputConverterOnUpdate, this);
if ($field != null && field !== void 0) {
if (Array.isArray($field.values())) {
const n = Math.max(-1, ...Object.keys(field.fields || {}).map(Number));
getObservableMapValues($field.fields).forEach(($f) => {
if (Number($f.name) > n) {
$field.$changed++;
$field.state.form.$changed++;
$field.fields.delete($f.name);
}
});
}
if (field?.fields) {
const fallback = this.state.options.get(OptionsEnum.fallback);
const x = this.state
.struct()
.findIndex((s) => s.startsWith($field.path.replace(/\.\d+\./, "[].") + "[]"));
if (!fallback && $field.fields.size === 0 && x < 0) {
$field.value = parseInput(applyInputConverterOnUpdate ? $field.$input : (val) => val, {
fallbackValueOption: this.state.options.get(OptionsEnum.fallbackValue, this),
separated: get(raw, $path),
});
return;
}
}
if (field === null || field.fields == null) {
$field.value = parseInput(applyInputConverterOnUpdate ? $field.$input : (val) => val, {
fallbackValueOption: this.state.options.get(OptionsEnum.fallbackValue, this),
separated: field,
});
return;
}
}
if ($container != null && $field == null) {
// get full path when using update() with select() - FIX: #179
const $newFieldPath = [this.path, $path].join(".").replace(/^\.+/, "");
// init field into the container field
$container.$changed++;
$container.state.form.$changed++;
$container.initField($key, $newFieldPath, field, true);
}
else if (recursion) {
if (has(field, FieldPropsEnum.fields) && field.fields != null) {
// handle nested fields if defined
this.deepUpdate(field.fields, $path);
}
else {
// handle nested fields if undefined or null
const $fields = pathToFieldsTree(this.state.struct(), $path);
this.deepUpdate($fields, $path, false);
}
}
});
}
/**
Get Fields Props
*/
get(prop = null, strict = true) {
if (prop == null) {
return this.deepGet([...props.computed, ...props.editable, ...props.validation], this.fields, strict);
}
allowedProps(AllowedFieldPropsTypes.all, Array.isArray(prop) ? prop : [prop]);
if (typeof prop === 'string') {
if ([FieldPropsEnum.hooks, FieldPropsEnum.handlers].includes(prop)) {
return this[`$${prop}`];
}
if (strict && this.fields.size === 0) {
const retrieveNullifiedEmptyStrings = this.state.options.get(OptionsEnum.retrieveNullifiedEmptyStrings, this);
return parseCheckOutput(this, prop, strict ? retrieveNullifiedEmptyStrings : false);
}
const value = this.deepGet(prop, this.fields, strict);
const removeNullishValuesInArrays = this.state.options.get(OptionsEnum.removeNullishValuesInArrays, this);
return parseCheckArray(this, value, prop, strict ? removeNullishValuesInArrays : false);
}
return this.deepGet(prop, this.fields, strict);
}
/**
Get Fields Props Recursively
*/
deepGet(prop, fields, strict = true) {
const _fieldsArr = getObservableMapValues(fields);
const _result = transform(_fieldsArr, (obj, field) => {
const $nested = ($fields) => $fields.size !== 0 ? this.deepGet(prop, $fields, strict) : undefined;
Object.assign(obj, {
[field.key]: { fields: $nested(field.fields) },
});
if (typeof prop === 'string') {
const opt = this.state.options;
const removeProp = (opt.get(OptionsEnum.retrieveOnlyDirtyFieldsValues, this) &&
prop === FieldPropsEnum.value &&
field.isPristine) ||
(opt.get(OptionsEnum.retrieveOnlyEnabledFieldsValues, this) &&
prop === FieldPropsEnum.value &&
field.disabled) ||
(opt.get(OptionsEnum.retrieveOnlyEnabledFieldsErrors, this) &&
prop === FieldPropsEnum.error &&
field.disabled &&
field.isValid &&
(!field.error || !field.hasError)) ||
(opt.get(OptionsEnum.softDelete, this) &&
prop === FieldPropsEnum.value &&
field.deleted);
if (field.fields.size === 0) {
delete obj[field.key];
if (removeProp)
return obj;
const retrieveNullifiedEmptyStrings = this.state.options.get(OptionsEnum.retrieveNullifiedEmptyStrings, this);
return Object.assign(obj, {
[field.key]: parseCheckOutput(field, prop, strict ? retrieveNullifiedEmptyStrings : false),
});
}
let value = this.deepGet(prop, field.fields, strict);
if (prop === FieldPropsEnum.value)
value = field.$output(value);
delete obj[field.key];
if (removeProp)
return obj;
const removeNullishValuesInArrays = this.state.options.get(OptionsEnum.removeNullishValuesInArrays, this);
return Object.assign(obj, {
[field.key]: parseCheckArray(field, value, prop, strict ? removeNullishValuesInArrays : false),
});
}
each(prop, ($prop) => Object.assign(obj[field.key], {
[$prop]: field[$prop],
}));
return obj;
}, {});
// Array fields with integer keys must preserve _entries order
// (JavaScript Object.assign sorts integer-like keys regardless of insertion order)
if (typeof prop === "string" && _fieldsArr.length > 0 && hasIntKeys(fields)) {
const keysInResult = new Set(Object.keys(_result));
return _fieldsArr
.filter(f => keysInResult.has(String(f.key)))
.map(f => _result[String(f.key)]);
}
return _result;
}
/**
Set Fields Props
*/
set(prop, data) {
// UPDATE CUSTOM PROP
if (typeof prop === 'string' && data !== void 0) {
allowedProps(AllowedFieldPropsTypes.editable, [prop]);
const isPlain = [FieldPropsEnum.hooks, FieldPropsEnum.handlers].includes(prop);
const deep = (data !== null && typeof data === 'object' && prop === FieldPropsEnum.value) ||
(isPlainObject(data) && !isPlain);
if (deep && this.hasNestedFields)
return this.deepSet(prop, data, "", true);
if (prop === FieldPropsEnum.value) {
const applyInputConverterOnSet = this.state.options.get(OptionsEnum.applyInputConverterOnSet, this);
this.value = parseInput(applyInputConverterOnSet ? this.$input : (val) => val, {
fallbackValueOption: this.state.options.get(OptionsEnum.fallbackValue, this),
separated: data,
});
}
else if (isPlain) {
Object.assign(this[`$${prop}`], data);
}
else {
set(this, `$${prop}`, data);
}
return;
}
// NO PROP NAME PROVIDED ("prop" is value)
if (data == null) {
if (this.hasNestedFields)
this.deepSet(FieldPropsEnum.value, prop, "", true);
else
this.set(FieldPropsEnum.value, prop);
}
}
/**
Set Fields Props Recursively
*/
deepSet(prop, data, path = "", recursion = false) {
const err = "You are updating a not existent field:";
const isStrict = this.state.options.get(OptionsEnum.strictSet, this);
if (data == null) {
this.each((field) => (field.$value = defaultValue({
fallbackValueOption: this.state.options.get(OptionsEnum.fallbackValue, this),
value: field.$value,
nullable: field.$nullable,
type: field.type,
})));
return;
}
each(data, ($val, $key) => {
const $path = `${path}.${$key}`.replace(/^\.+/, "");
// get the field by path joining keys recursively
const field = this.select($path, null, isStrict);
// if no field found when is strict update, throw error
if (isStrict)
throwError($path, field, err);
// update the field/fields if defined
if (field !== void 0) {
// update field values or others props
if ($val !== void 0) {
field.set(prop, $val, recursion);
}
// update values recursively only if field has nested
if (field.fields.size && $val !== null && typeof $val === 'object') {
this.deepSet(prop, $val, $path, recursion);
}
}
});
}
/**
Add Field
*/
add(obj, execEvent = true) {
if (isArrayOfObjects(obj)) {
each(obj, (values) => this.update({
[maxKey(this.fields)]: values,
}));
this.$changed++;
this.state.form.$changed++;
execEvent && this.execHook(FieldPropsEnum.onAdd);
return this;
}
let key;
if (has(obj, FieldPropsEnum.key))
key = obj.key;
if (has(obj, FieldPropsEnum.name))
key = obj.name;
if (!key)
key = maxKey(this.fields);
const $path = ($key) => [this.path, $key].join(".").replace(/^\.+/, "");
const tree = pathToFieldsTree(this.state.struct(), this.path ?? '', 0, true);
const field = this.initField(key, $path(key), merge(tree[0], obj));
const hasValues = has(obj, FieldPropsEnum.value) || has(obj, FieldPropsEnum.fields);
if (!hasValues &&
!this.state.options.get(OptionsEnum.preserveDeletedFieldsValues, this)) {
const fallbackValueOption = this.state.options.get(OptionsEnum.fallbackValue, this);
field.$value = defaultValue({
fallbackValueOption,
value: field.$value,
nullable: field.$nullable,
type: field.type,
});
field.each((field) => (field.$value = defaultValue({
fallbackValueOption,
value: field.$value,
nullable: field.$nullable,
type: field.type,
})));
}
this.$changed++;
this.state.form.$changed++;
execEvent && this.execHook(FieldPropsEnum.onAdd);
return field;
}
/**
Del Field
*/
del($path = null, execEvent = true) {
const isStrict = this.state.options.get(OptionsEnum.strictDelete, this);
const path = parsePath($path ?? this.path ?? '');
const fullpath = [this.path ?? '', path ?? ''].join(".").replace(/^\.+|\.+$/g, "");
const container = this.container($path);
const keys = path.split(".");
const lastKey = keys[keys.length - 1];
if (isStrict && !container.fields.has(lastKey)) {
const msg = `Key "${lastKey}" not found when trying to delete field`;
throwError(fullpath, null, msg);
}
container.$changed++;
container.state.form.$changed++;
if (this.state.options.get(OptionsEnum.softDelete, this)) {
return this.select(fullpath).set(FieldPropsEnum.deleted, true);
}
container.each((field) => field.debouncedValidation.cancel());
execEvent && this.execHook(FieldPropsEnum.onDel);
return container.fields.delete(lastKey);
}
/******************************************************************
Events
*/
/**
MobX Event (observe/intercept)
*/
MOBXEvent({ prop = FieldPropsEnum.value, key = null, path = null, call, type, }) {
let $prop = key || prop;
allowedProps(AllowedFieldPropsTypes.observable, [$prop]);
const $instance = this.select(path || this.path, null, false) || this;
const $call = (change) => call.apply(null, [
{
change,
form: this.state.form,
path: $instance.path || null,
field: $instance.path ? $instance : null,
},
]);
let fn;
let ffn;
if (type === "observer") {
fn = observe;
ffn = (cb) => observe($instance.fields.toArray(), cb); // fields
}
if (type === "interceptor") {
$prop = `$${prop}`;
fn = intercept;
ffn = $instance.fields.intercept; // fields
}
const $dkey = $instance.path ? `${$prop}@${$instance.path}` : $prop;
merge(this.state.disposers[type], {
[$dkey]: $prop === FieldPropsEnum.fields
? ffn.apply((change) => $call(change))
: fn($instance, $prop, (change) => $call(change)),
});
}
/**
Dispose MOBX Events
*/
dispose(opt = null) {
if (this.path && opt)
return this.disposeSingle(opt);
return this.disposeAll();
}
/**
Dispose All Events (observe/intercept)
*/
disposeAll() {
const dispose = (disposer) => disposer.apply();
each(this.state.disposers.interceptor, dispose);
each(this.state.disposers.observer, dispose);
this.state.disposers = { interceptor: {}, observer: {} };
return;
}
/**
Dispose Single Event (observe/intercept)
*/
disposeSingle({ type, key = FieldPropsEnum.value, path = null }) {
const $path = parsePath(path ?? this.path ?? '');
// eslint-disable-next-line
if (type === "interceptor")
key = `$${key}`; // target observables
const disposersType = this.state.disposers[type];
disposersType[`${key}@${$path}`].apply();
delete disposersType[`${key}@${$path}`];
}
/******************************************************************
Utils
*/
/**
Fields Selector
*/
select(path, fields = null, isStrict = true) {
const $path = parsePath(String(path));
const keys = $path.split(".");
const headKey = keys[0];
keys.shift();
let $fields = fields == null ? this.fields.get(headKey) : fields.get(headKey);
let stop = false;
each(keys, ($key) => {
if (stop)
return;
if ($fields == null) {
$fields = undefined;
stop = true;
}
else {
$fields = $fields.fields.get($key);
}
});
if (isStrict && this.state.options.get(OptionsEnum.strictSelect, this)) {
throwError(String(path), $fields);
}
return $fields;
}
/**
Get Container
*/
container($path) {
const path = parsePath($path ?? this.path ?? '');
const cpath = path.replace(/[^./]+$/, "").replace(/^\.+|\.+$/g, "");
if (!!this.path && $path == null) {
return cpath !== ""
? this.state.form.select(cpath, null, false)
: this.state.form;
}
return cpath !== "" ? this.select(cpath, null, false) : this;
}
/**
Has Field
*/
has(path) {
return this.fields.has(path);
}
/**
Map Fields
*/
map(cb) {
return getObservableMapValues(this.fields).map(cb);
}
/**
* Iterates deeply over fields and invokes `iteratee` for each element.
* The iteratee is invoked with three arguments: (value, index|key, depth).
*
* @param {Function} iteratee The function invoked per iteration.
* @param {Array|Object} [fields=form.fields] fields to iterate over.
* @param {number} [depth=1] The recursion depth for internal use.
* @returns {Array} Returns [fields.values()] of input [fields] parameter.
* @example
*
* JSON.stringify(form)
* // => {
* "fields": {
* "state": {
* "fields": {
* "city": {
* "fields": { "places": {
* "fields": {},
* "key": "places", "path": "state.city.places", "$value": "NY Places"
* }
* },
* "key": "city", "path": "state.city", "$value": "New York"
* }
* },
* "key": "state", "path": "state", "$value": "USA"
* }
* }
* }
*
* const data = {};
* form.each(field => data[field.path] = field.value);
* // => {
* "state": "USA",
* "state.city": "New York",
* "state.city.places": "NY Places"
* }
*
*/
each(iteratee, fields = null, depth = 0) {
const $fields = fields || this.fields;
getObservableMapValues($fields).forEach((field, index) => {
iteratee(field, index, depth);
if (field.fields.size !== 0) {
this.each(iteratee, field.fields, depth + 1);
}
});
}
reduce(iteratee, acc) {
return getObservableMapValues(this.fields).reduce(iteratee, acc);
}
/******************************************************************
Array Operations
*/
/**
Move a field from one index to another (for sortable lists).
*/
move(fromIndex, toIndex) {
this.fields.move(fromIndex, toIndex);
}
/******************************************************************
Helpers
*/
/**
Fields Selector (alias of select)
*/
$(key) {
return this.select(key);
}
/**
Fields Values (recursive with Nested Fields)
*/
values() {
return this.get(FieldPropsEnum.value);
}
/**
Fields Errors (recursive with Nested Fields)
*/
errors() {
return this.get(FieldPropsEnum.error);
}
/**
Fields Labels (recursive with Nested Fields)
*/
labels() {
return this.get(FieldPropsEnum.label);
}
/**
Fields Placeholders (recursive with Nested Fields)
*/
placeholders() {
return this.get(FieldPropsEnum.placeholder);
}
/**
Fields Default Values (recursive with Nested Fields)
*/
defaults() {
return this.get(FieldPropsEnum.default);
}
/**
Fields Initial Values (recursive with Nested Fields)
*/
initials() {
return this.get(FieldPropsEnum.initial);
}
/**
Fields Types (recursive with Nested Fields)
*/
types() {
return this.get(FieldPropsEnum.type);
}
}
export { Base as default };
//# sourceMappingURL=Base.js.map