UNPKG

mobx-form

Version:
1,333 lines (1,130 loc) 39.6 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('mobx')) : typeof define === 'function' && define.amd ? define(['exports', 'mobx'], factory) : (global = global || self, factory(global.MobxForm = {}, global.mobx)); }(this, (function (exports, mobx) { 'use strict'; function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread2(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var jqTrim = function ( str ) { return ((str || '') + '').trim(); }; var now = function () { return Date.now(); }; /** * returns a new function than will be called after "ms" number of milliseconds * after the last call to it * * This is useful to execute a function that might occur too often * * @method debounce * @static * @param f {Function} the function to debounce * @param ms {Number} the number of milliseconds to wait. If any other call * is made before that threshold the waiting will be restarted * @param [ctx=undefined] {Object} the context on which this function will be executed * (the 'this' object inside the function wil be set to context) * @param [immediate=undefined] {Boolean} if the function should be executed in the leading edge or the trailing edge * ``` */ var debouncy = function debounce( f, ms, ctx, immediate ) { var ts, fn; var timeout = null; var args; fn = function () { ctx = ctx || this; args = arguments; ts = now(); var later = function () { var diff = now() - ts; if ( diff < ms ) { timeout = setTimeout( later, ms - diff ); return; } timeout = null; if ( !immediate ) { f.apply( ctx, args ); } }; if ( timeout === null ) { if ( immediate ) { f.apply( ctx, args ); } timeout = setTimeout( later, ms ); } }; fn.cancel = function () { clearTimeout( timeout ); timeout = null; // properly clear the timeout so the next call to fn works as expected }; return fn; }; var isNullishOrEmpty = function isNullishOrEmpty(value) { return typeof value === 'undefined' || value === null || value === ''; }; /** * Field class provides abstract the validation of a single field */ var Field = /*#__PURE__*/function () { _createClass(Field, [{ key: "resetInteractedFlag", value: function resetInteractedFlag() { this._interacted = false; } }, { key: "markAsInteracted", value: function markAsInteracted() { this._interacted = true; } }, { key: "resetValidatedOnce", value: function resetValidatedOnce() { this._validatedOnce = false; } }, { key: "_setValueOnly", value: function _setValueOnly(val) { if (!this._interacted) { this._interacted = true; } if (this._value === val) { return; } this._value = val; } }, { key: "_setValue", value: function _setValue(val) { if (this._value !== val && this._clearErrorOnValueChange && !this.valid) { this.resetError(); } this._setValueOnly(val); if (this._autoValidate) { this._debouncedValidation(); } } /** * setter for the value of the field */ }, { key: "setValue", /** * set the value of the field, optionaly * reset the errorMessage and interacted flags * * @param {any} value * @param { object} params the options object * @param {Boolean} params.resetInteractedFlag whether or not to reset the interacted flag * */ value: function setValue(value) { var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, resetInteractedFlag = _ref.resetInteractedFlag, commit = _ref.commit; if (resetInteractedFlag) { this._setValueOnly(value); this.rawError = undefined; this._interacted = false; } else { this._setValue(value); } if (commit) { this.commit(); } } /** * Restore the initial value of the field */ }, { key: "restoreInitialValue", value: function restoreInitialValue() { var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, _ref2$resetInteracted = _ref2.resetInteractedFlag, resetInteractedFlag = _ref2$resetInteracted === void 0 ? true : _ref2$resetInteracted, _ref2$commit = _ref2.commit, commit = _ref2$commit === void 0 ? true : _ref2$commit; this.setValue(this._initialValue, { resetInteractedFlag: resetInteractedFlag, commit: commit }); } }, { key: "commit", value: function commit() { this._initialValue = this.value; } /** * clear the valid state of the field by * removing the errorMessage string. A field is * considered valid if the errorMessage is not empty */ }, { key: "resetError", value: function resetError() { this.rawError = undefined; } }, { key: "clearValidation", value: function clearValidation() { this.resetError(); } /** * mark the field as already blurred so validation can * start to be applied to the field. */ }, { key: "_doValidate", value: function () { var _doValidate2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { var _validateFn, model, ret, i, vfn, _ret; return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _validateFn = this._validateFn, model = this.model; if (_validateFn) { _context.next = 3; break; } return _context.abrupt("return", Promise.resolve(true)); case 3: if (!Array.isArray(_validateFn)) { _context.next = 25; break; } i = 0; case 5: if (!(i < _validateFn.length)) { _context.next = 23; break; } vfn = _validateFn[i]; if (!(typeof vfn !== 'function')) { _context.next = 9; break; } throw new Error('Validator must be a function or a function[]'); case 9: _context.prev = 9; _context.next = 12; return vfn(this, model.fields, model); case 12: ret = _context.sent; if (!(ret === false || ((_ret = ret) === null || _ret === void 0 ? void 0 : _ret.error))) { _context.next = 15; break; } return _context.abrupt("return", ret); case 15: _context.next = 20; break; case 17: _context.prev = 17; _context.t0 = _context["catch"](9); return _context.abrupt("return", Promise.reject(_context.t0)); case 20: i++; _context.next = 5; break; case 23: _context.next = 32; break; case 25: _context.prev = 25; ret = _validateFn(this, model.fields, model); _context.next = 32; break; case 29: _context.prev = 29; _context.t1 = _context["catch"](25); return _context.abrupt("return", Promise.reject(_context.t1)); case 32: return _context.abrupt("return", ret); case 33: case "end": return _context.stop(); } } }, _callee, this, [[9, 17], [25, 29]]); })); function _doValidate() { return _doValidate2.apply(this, arguments); } return _doValidate; }() }, { key: "setDisabled", value: function setDisabled(disabled) { if (disabled) { this.resetError(); } this._disabled = disabled; } }, { key: "_validate", /** * validate the field. If force is true the validation will be perform * even if the field was not initially interacted or blurred * * @param params {object} arguments object * @param params.force {boolean} [force=false] */ value: function _validate() { var _this = this; var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, _ref3$force = _ref3.force, force = _ref3$force === void 0 ? false : _ref3$force; var required = this.required; if (!this._validatedOnce) { this._validatedOnce = true; } var shouldSkipValidation = this.disabled || !required && !this._validateFn; if (shouldSkipValidation) return; if (!force) { var userDidntInteractedWithTheField = !this._interacted; if (userDidntInteractedWithTheField && !this.hasValue) { // if we're not forcing the validation // and we haven't interacted with the field // we asume this field pass the validation status this.resetError(); return; } // if the field requires the user to lost focus before starting the validation // we wait until the field is marked as blurredOnce. Except in the case the // field has an error already in which case we do want to execute the validation if (this.waitForBlur && !this._blurredOnce && !this.errorMessage) { return; } } else { this._blurredOnce = true; } if (required) { if (!this.hasValue) { // we can indicate that the field is required by passing the error message as the value of // the required field. If we pass a boolean or a function then the value of the error message // can be set in the requiredMessage field of the validator descriptor this.setError({ message: typeof this._required === 'string' ? this._required : "Field: \"".concat(this.name, "\" is required") }); return; } this.resetError(); } this.setValidating(true); var validationTs = this._validationTs = Date.now(); var res = this._doValidate(); // eslint-disable-next-line consistent-return return new Promise(function (resolve) { res.then(mobx.action(function (res_) { if (validationTs !== _this._validationTs) return; // ignore stale validations _this.setValidating(false); // if the function returned a boolean we assume it is // the flag for the valid state if (typeof res_ === 'boolean') { _this.setErrorMessage(res_ ? undefined : _this.originalErrorMessage); resolve(); return; } if (res_ && res_.error) { _this.setErrorMessage(res_.error); resolve(); return; } _this.resetError(); resolve(); // we use this to chain validators }), mobx.action(function () { var errorArg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; if (validationTs !== _this._validationTs) return; // ignore stale validations _this.setValidating(false); var error = errorArg.error, message = errorArg.message; var errorToSet = errorArg; if (!message) { errorToSet = _objectSpread2(_objectSpread2({}, errorToSet), {}, { message: message || _this.originalErrorMessage }); } if (error) { errorToSet = _objectSpread2(_objectSpread2({}, errorToSet), {}, { message: error }); } _this.setError(errorToSet); resolve(); // we use this to chain validators })); }); } }, { key: "setErrorMessage", value: function setErrorMessage(msg) { if (jqTrim(msg) === '') { msg = undefined; } if (!msg) { this.resetError(); } else { this.setError({ message: msg }); } } }, { key: "setError", value: function setError(error) { this.rawError = error; } }, { key: "validatedAtLeastOnce", get: function get() { return this._validatedOnce; } }, { key: "waitForBlur", get: function get() { return !!this._waitForBlur; } }, { key: "disabled", get: function get() { return !!this._disabled; } }, { key: "required", get: function get() { if (this.disabled) return false; return !!this._required; } }, { key: "hasValue", get: function get() { if (this._hasValueFn) { return this._hasValueFn(this.value); } // consider the case where the value is an array // we consider it actually has a value if the value is defined // and the array is not empy if (Array.isArray(this.value)) { return this.value.length > 0; } return !isNullishOrEmpty(this.value); } /** * flag to know if a validation is in progress on this field */ }, { key: "blurred", get: function get() { return !!this._blurredOnce; } /** the raw error in caes validator throws a real error */ }, { key: "errorMessage", /** * the error message associated with this field. * This is used to indicate what error happened during * the validation process */ get: function get() { var _this$rawError; return (_this$rawError = this.rawError) === null || _this$rawError === void 0 ? void 0 : _this$rawError.message; } /** * whether the validation should be launch after a * new value is set in the field. This is usually associated * to forms that set the value on the fields after each * onChange event */ }, { key: "autoValidate", get: function get() { return this._autoValidate; } /** * used to keep track of the original message */ }, { key: "valid", /** * whether the field is valid or not */ get: function get() { return !this.errorMessage; } /** * whether the user has interacted or not with the field */ }, { key: "interacted", get: function get() { return this._interacted; } /** * get the value set on the field */ }, { key: "value", get: function get() { return this._value; }, set: function set(val) { this._setValue(val); } }, { key: "dirty", get: function get() { return this._initialValue !== this.value; } }, { key: "originalErrorMessage", get: function get() { return this._originalErrorMessage || "Validation for \"".concat(this.name, "\" failed"); } }, { key: "validating", get: function get() { return this._validating; } }, { key: "error", get: function get() { return this.errorMessage; } }]); function Field(model, value) { var _this2 = this; var validatorDescriptor = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var fieldName = arguments.length > 3 ? arguments[3] : undefined; _classCallCheck(this, Field); this._disabled = void 0; this._required = void 0; this._validatedOnce = false; this._validating = false; this._initialValue = void 0; this._value = void 0; this._interacted = void 0; this._blurredOnce = false; this.rawError = void 0; this._autoValidate = false; this._originalErrorMessage = void 0; this.markBlurredAndValidate = function () { if (!_this2._blurredOnce) { _this2._blurredOnce = true; } _this2.validate(); }; this.validate = function (opts) { _this2._debouncedValidation.cancel(); return _this2._validate(opts); }; this.setValidating = function (validating) { _this2._validating = validating; }; this.setRequired = function (val) { _this2._required = val; }; mobx.makeObservable(this, { resetValidatedOnce: mobx.action, _validatedOnce: mobx.observable, validatedAtLeastOnce: mobx.computed, _disabled: mobx.observable, _required: mobx.observable, waitForBlur: mobx.computed, disabled: mobx.computed, required: mobx.computed, resetInteractedFlag: mobx.action, markAsInteracted: mobx.action, hasValue: mobx.computed, _autoValidate: mobx.observable, _value: mobx.observable, _initialValue: mobx.observable, _interacted: mobx.observable, _blurredOnce: mobx.observable, dirty: mobx.computed, blurred: mobx.computed, errorMessage: mobx.computed, rawError: mobx.observable.ref, setError: mobx.action, resetError: mobx.action, error: mobx.computed, autoValidate: mobx.computed, valid: mobx.computed, validating: mobx.computed, _validating: mobx.observable, setValidating: mobx.action, interacted: mobx.computed, _setValueOnly: mobx.action, _setValue: mobx.action, setValue: mobx.action, restoreInitialValue: mobx.action, commit: mobx.action, clearValidation: mobx.action, markBlurredAndValidate: mobx.action, _doValidate: mobx.action, setDisabled: mobx.action, validate: mobx.action, originalErrorMessage: mobx.computed, _validate: mobx.action, setRequired: mobx.action, setErrorMessage: mobx.action }); var DEBOUNCE_THRESHOLD = 300; this._value = value; this.model = model; this.name = fieldName; this._initialValue = value; var waitForBlur = validatorDescriptor.waitForBlur, disabled = validatorDescriptor.disabled, errorMessage = validatorDescriptor.errorMessage, validator = validatorDescriptor.validator, hasValue = validatorDescriptor.hasValue, required = validatorDescriptor.required, _validatorDescriptor$ = validatorDescriptor.autoValidate, autoValidate = _validatorDescriptor$ === void 0 ? true : _validatorDescriptor$, meta = validatorDescriptor.meta, _validatorDescriptor$2 = validatorDescriptor.validationDebounceThreshold, validationDebounceThreshold = _validatorDescriptor$2 === void 0 ? DEBOUNCE_THRESHOLD : _validatorDescriptor$2, clearErrorOnValueChange = validatorDescriptor.clearErrorOnValueChange; this._debouncedValidation = debouncy(this._validate, validationDebounceThreshold); this._waitForBlur = waitForBlur; this._originalErrorMessage = errorMessage; this._validateFn = validator; this._clearErrorOnValueChange = clearErrorOnValueChange; // useful to determine if the field has a value set // only used if provided this._hasValueFn = hasValue; this._required = required; this._autoValidate = autoValidate; this._disabled = disabled; this.meta = meta; // store other props passed on the fields } return Field; }(); var toString = Object.prototype.toString; var isObject = function isObject(o) { return o && toString.call(o) === '[object Object]'; }; /** * a helper class to generate a dynamic form * provided some keys and validators descriptors * * @export * @class FormModel */ var FormModel = /*#__PURE__*/function () { _createClass(FormModel, [{ key: "restoreInitialValues", /** * Restore the initial values set at the creation time of the model * */ value: function restoreInitialValues(opts) { this._eachField(function (field) { return field.restoreInitialValue(opts); }); } }, { key: "commit", value: function commit() { this._eachField(function (field) { return field.commit(); }); } }, { key: "updateFrom", /** * Set multiple values to more than one field a time using an object * where each key is the name of a field. The value will be set to each * field and from that point on the values set are considered the new * initial values. Validation and interacted flags are also reset if the second argument is true * */ value: function updateFrom(obj) { var _this = this; var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var _ref$resetInteractedF = _ref.resetInteractedFlag, resetInteractedFlag = _ref$resetInteractedF === void 0 ? true : _ref$resetInteractedF, opts = _objectWithoutProperties(_ref, ["resetInteractedFlag"]); Object.keys(obj).forEach(function (key) { return _this.updateField(key, obj[key], _objectSpread2({ resetInteractedFlag: resetInteractedFlag }, opts)); }); } /** * return the array of errors found. The array is an Array<String> * */ }, { key: "validate", /** * Manually perform the form validation * */ value: function validate() { var _this2 = this; this._validating = true; return Promise.all(this._fieldKeys().map(function (key) { var field = _this2.fields[key]; return Promise.resolve(field.validate({ force: true })); })).then(function () { _this2.setValidating(false); }).catch(function () { _this2.setValidating(false); }); } /** * Update the value of the field identified by the provided name. * Optionally if reset is set to true, interacted and * errorMessage are cleared in the Field. * */ }, { key: "updateField", value: function updateField(name, value) { var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var throwIfMissingField = opts.throwIfMissingField, restOpts = _objectWithoutProperties(opts, ["throwIfMissingField"]); var theField = this._getField(name, { throwIfMissingField: throwIfMissingField }); theField === null || theField === void 0 ? void 0 : theField.setValue(value, restOpts); } /** * return the data as plain Javascript object (mobx magic removed from the fields) * */ }, { key: "validatedAtLeastOnce", get: function get() { var _this3 = this; var keys = Object.keys(this.fields); return keys.every(function (key) { return _this3.fields[key].validatedAtLeastOnce; }); } }, { key: "dataIsReady", get: function get() { return this.interacted && this.requiredAreFilled && this.valid; } }, { key: "requiredFields", get: function get() { var _this4 = this; var keys = Object.keys(this.fields); return keys.filter(function (key) { return _this4.fields[key].required; }); } }, { key: "requiredAreFilled", get: function get() { var _this5 = this; var keys = Object.keys(this.fields); return keys.every(function (key) { var field = _this5.fields[key]; if (field.required) { return !!field.hasValue; } return true; }); } }, { key: "valid", // flag to indicate whether the form is valid or not // since some of the validators might be async validators // this value might be false until the validation process finish get: function get() { var _this6 = this; if (this._validating) { return false; // consider the form invalid until the validation process finish } var keys = Object.keys(this.fields); return keys.every(function (key) { var field = _this6.fields[key]; return !!field.valid; }); } /** * whether or not the form has been "interacted", meaning that at * least a value has set on any of the fields after the model * has been created */ }, { key: "interacted", get: function get() { var _this7 = this; var keys = this._fieldKeys(); return keys.some(function (key) { var field = _this7.fields[key]; return !!field.interacted; }); } }, { key: "dirty", get: function get() { var _this8 = this; return this._fieldKeys().some(function (key) { var f = _this8._getField(key); return f.dirty; }); } }, { key: "summary", get: function get() { var _this9 = this; return this._fieldKeys().reduce(function (seq, key) { var field = _this9.fields[key]; if (field.errorMessage) { seq.push(field.errorMessage); } return seq; }, []); } }, { key: "validating", get: function get() { var _this10 = this; return this._validating || this._fieldKeys().some(function (key) { var f = _this10._getField(key); return f.validating; }); } }, { key: "serializedData", get: function get() { var _this11 = this; var keys = Object.keys(this.fields); return mobx.toJS(keys.reduce(function (seq, key) { var field = _this11.fields[key]; var value = mobx.toJS(field.value); // this is required to make sure forms that use the serializedData object // have the values without leading or trailing spaces seq[key] = typeof value === 'string' ? jqTrim(value) : value; return seq; }, {})); } /** * Creates an instance of FormModel. * * @param {Object|Array} [descriptors={}] * @param {Object} [initialState={}] * * initialState => an object which keys are the names of the fields and the values the initial values for the form. * validators => an object which keys are the names of the fields and the values are the descriptors for the validators */ }]); function FormModel() { var _this12 = this; var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, _ref2$descriptors = _ref2.descriptors, descriptors = _ref2$descriptors === void 0 ? {} : _ref2$descriptors, initialState = _ref2.initialState, _ref2$options = _ref2.options, options = _ref2$options === void 0 ? {} : _ref2$options; _classCallCheck(this, FormModel); this.fields = {}; this._validating = false; this.setValidating = function (validating) { _this12._validating = validating; }; this.addFields = function (fieldsDescriptor) { if (fieldsDescriptor == null || !isObject(fieldsDescriptor) && !Array.isArray(fieldsDescriptor)) { throw new Error('fieldDescriptor has to be an Object or an Array'); } if (Array.isArray(fieldsDescriptor)) { fieldsDescriptor.forEach(function (field) { var value = field.value, name = field.name, descriptor = _objectWithoutProperties(field, ["value", "name"]); _this12._createField({ value: value, name: name, descriptor: descriptor }); }); return; } var fieldsToAdd = Object.keys(fieldsDescriptor); fieldsToAdd.forEach(function (key) { var _fieldsDescriptor$key = fieldsDescriptor[key], value = _fieldsDescriptor$key.value, descriptor = _objectWithoutProperties(_fieldsDescriptor$key, ["value"]); _this12._createField({ value: value, name: key, descriptor: descriptor }); }); }; mobx.makeObservable(this, { resetValidatedOnce: mobx.action, validatedAtLeastOnce: mobx.computed, dataIsReady: mobx.computed, requiredFields: mobx.computed, requiredAreFilled: mobx.computed, fields: mobx.observable, _validating: mobx.observable, setValidating: mobx.action, validating: mobx.computed, valid: mobx.computed, interacted: mobx.computed, restoreInitialValues: mobx.action, updateFrom: mobx.action, summary: mobx.computed, validate: mobx.action, updateField: mobx.action, serializedData: mobx.computed, resetInteractedFlag: mobx.action, disableFields: mobx.action, addFields: mobx.action, enableFields: mobx.action, commit: mobx.action, dirty: mobx.computed }); this.addFields(descriptors); initialState && this.updateFrom(initialState, { throwIfMissingField: options.throwIfMissingField, commit: true }); } _createClass(FormModel, [{ key: "_getField", value: function _getField(name) { var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, _ref3$throwIfMissingF = _ref3.throwIfMissingField, throwIfMissingField = _ref3$throwIfMissingF === void 0 ? true : _ref3$throwIfMissingF; var theField = this.fields[name]; if (!theField && throwIfMissingField) { throw new Error("Field \"".concat(name, "\" not found")); } return theField; } }, { key: "_eachField", value: function _eachField(cb) { var _this13 = this; Object.keys(this.fields).forEach(function (key) { return cb(_this13.fields[key]); }); } }, { key: "_fieldKeys", value: function _fieldKeys() { return Object.keys(this.fields); } }, { key: "resetInteractedFlag", value: function resetInteractedFlag() { this._eachField(function (field) { return field.resetInteractedFlag(); }); } }, { key: "disableFields", value: function disableFields(fieldKeys) { var _this14 = this; if (!Array.isArray(fieldKeys)) throw new TypeError('fieldKeys should be an array with the names of the fields to disable'); fieldKeys.forEach(function (key) { var field = _this14._getField(key); field.setDisabled(true); }); } }, { key: "_createField", value: function _createField(_ref4) { var value = _ref4.value, name = _ref4.name, descriptor = _ref4.descriptor; mobx.extendObservable(this.fields, _defineProperty({}, name, new Field(this, value, descriptor, name))); } }, { key: "enableFields", value: function enableFields(fieldKeys) { var _this15 = this; if (!Array.isArray(fieldKeys)) throw new TypeError('fieldKeys should be an array with the names of the fields to disable'); fieldKeys.forEach(function (key) { var field = _this15._getField(key); field.setDisabled(false); }); } }, { key: "resetValidatedOnce", value: function resetValidatedOnce() { var _this16 = this; this._fieldKeys().forEach(function (key) { _this16.fields[key].resetValidatedOnce(); }); } }]); return FormModel; }(); /** * return an instance of a FormModel refer to the constructor * * @param {Object|Array} fieldDescriptors * @param {Object} initialState * @param {Object} options */ var createModel = function createModel(_ref5) { var descriptors = _ref5.descriptors, initialState = _ref5.initialState, options = _ref5.options; return new FormModel({ descriptors: descriptors, initialState: initialState, options: options }); }; var createModelFromState = function createModelFromState() { var initialState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var validators = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var stateKeys = Object.keys(initialState); var validatorsKeys = Object.keys(validators); var descriptors = Array.from(new Set([].concat(_toConsumableArray(stateKeys), _toConsumableArray(validatorsKeys))), function (key) { return _objectSpread2(_objectSpread2({}, validators[key] || {}), {}, { value: initialState[key], name: key }); }); return createModel({ descriptors: descriptors, options: options }); }; exports.FormModel = FormModel; exports.createModel = createModel; exports.createModelFromState = createModelFromState; Object.defineProperty(exports, '__esModule', { value: true }); })));