import { FormItem } from './FormItem'; import { FormValidator, FormErrors, FormValue, FormValueWatcher, FormValidationWatcher, FormProps, LayoutItem, FormItemsObject, FormLayout } from './Form.model'; import { FormItemTexts } from './FormItem.model'; export class Form { validators: FormValidator[]; formComponent: any /* HTMLNmafaFormElement */; layout: FormLayout; private _formItems: FormItem[]; private _formItemsObject: FormItemsObject; private _watchers: FormValueWatcher[] = []; private _value: FormValue = {}; private _disabled: boolean; private _errors: FormErrors; private _validationWatcher: FormValidationWatcher; constructor(props: FormProps) { this.validators = props.validators; props.layout.forEach((row: LayoutItem[]) => { row.forEach((layoutItem: LayoutItem) => { layoutItem.item.form = this; }); }); this.layout = props.layout; this._formItems = props.layout .reduce((currentRow: LayoutItem[], nextRow: LayoutItem[]) => { return [...currentRow, ...nextRow]; }, []) .map((layoutItem: LayoutItem) => layoutItem.item); this._formItemsObject = this._buildFormsItemsObject(this._formItems); } /* -------------- Public methods ---------------- */ /* ---------------------------------------------- */ getValue(): FormValue { return this._value; } setValue(value: FormValue) { if (this._disabled) { return; } this._formItems.forEach((formItem: FormItem) => { formItem._setValue(value[formItem.name]); }); this._setValue(value); } setValidationWatcher(fn: FormValidationWatcher) { this._validationWatcher = fn; } watchValue(cb: FormValueWatcher) { this._watchers.push(cb); } disable() { this.formComponent.disabled = true; this._disabled = true; this._formItems.forEach((formItem: FormItem) => { formItem._disable(); }); this._emptyErrors(); } enable() { this.formComponent.disabled = false; this._disabled = false; this._formItems.forEach((formItem: FormItem) => { formItem._enable(); }); this._calcErrors(); } setTexts(texts: Record) { this._formItems.forEach(formItem => { if (texts[formItem.name]) { formItem.setTexts(texts[formItem.name]); } }); this._calcErrors(); } isDisabled(): boolean { return this._disabled; } /* -------------- Internal Methods ---------------- */ /* ------------------------------------------------ */ _initValue(value: FormValue = {}) { this._value = value; } _setValue(value: FormValue) { this._value = value; this._watchers.forEach(cb => { cb(value); }); this.formComponent.value = value; this._calcErrors(); } _getFormItems(): FormItem[] { return this._formItems; } _calcErrors() { if (!this.formComponent) { return; } let errors = {}; if (this.validators && this.validators.length && !this._disabled) { this.validators.forEach((validator: FormValidator) => { const errorMsg = validator.fn(this._getFormsItemsObject()); if (errorMsg) { errors = { ...errors, [validator.name]: errorMsg }; } }); } this._setErrors(errors); this.formComponent.errors = errors; } _setErrors(errors: FormErrors) { this._errors = errors; if (this._validationWatcher) { this._validationWatcher(this._isValid()); } } _getFormsItemsObject(): Record { return this._formItemsObject; } _buildFormsItemsObject(formItems: FormItem[]): Record { return formItems.reduce((prev, next) => { return { ...prev, [next.name]: next }; }, {}); } _emptyErrors() { this._setErrors({}); this.formComponent.errors = {}; } _updateValidators(validators: FormValidator[]) { if (!this.formComponent) { return; } this.formComponent.validators = validators; } _isValid(): boolean { const hasInvalidFormItem = this._formItems.some(formItem => !formItem.isValid()); return !hasInvalidFormItem && Object.keys(this._errors).length === 0; } }