UNPKG

@datorama/akita-ng-forms-manager

Version:

The best way to manage your Angular form state in Akita

280 lines 11.1 kB
import * as tslib_1 from "tslib"; import { Injectable, Inject, Optional } from '@angular/core'; import { FormArray, FormControl, FormGroup } from '@angular/forms'; import { coerceArray, filterNil, logAction } from '@datorama/akita'; import { merge } from 'rxjs'; import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'; import { FormsQuery } from './forms-manager.query'; import { FormsStore } from './forms-manager.store'; import { FORMS_MANAGER_OPTIONS, defaultOptions } from './forms-manager-options'; import * as i0 from "@angular/core"; import * as i1 from "./forms-manager-options"; let AkitaNgFormsManager = class AkitaNgFormsManager { constructor(options = {}) { this.valueChanges = {}; this.ngForms = {}; this._options = Object.assign({}, defaultOptions, options); this._store = new FormsStore({}); this._query = new FormsQuery(this.store); } get query() { return this._query; } get store() { return this._store; } selectValid(formName, path) { return this.selectControl(formName, path).pipe(map(control => control.valid)); } selectDirty(formName, path) { return this.selectControl(formName, path).pipe(map(control => control.dirty)); } selectDisabled(formName, path) { return this.selectControl(formName, path).pipe(map(control => control.disabled)); } selectValue(formName, path) { return this.selectControl(formName, path).pipe(map(control => control.value)); } selectErrors(formName, path) { return this.selectControl(formName, path).pipe(map(control => control.errors)); } selectNgForm(formName) { return this.selectForm(formName, { filterNil: true }).pipe(map(() => this.ngForms[formName])); } /** * If no path specified it means that it's a single FormControl or FormArray */ selectControl(formName, path) { if (!path) { return this.selectForm(formName); } return this.query .select(state => state[formName]) .pipe(filterNil, map(form => this.resolveControl(form, path)), distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b))); } getControl(formName, path) { if (!path) { return this.getForm(formName); } if (this.hasForm(formName)) { const form = this.getForm(formName); return this.resolveControl(form, path); } return null; } selectForm(formName, options = { filterNil: true }) { return this.query.select(state => state[formName]).pipe(options.filterNil ? filterNil : s => s); } getForm(formName) { return this.query.getValue()[formName]; } getNgForm(formName) { return this.ngForms[formName]; } hasForm(formName) { return !!this.getForm(formName); } upsert(formName, form, config = {}) { const merged = Object.assign({ debounceTime: this._options.debounceTime, emitEvent: false }, config); /** If the form already exist, patch the form with the store value */ if (this.hasForm(formName) === true) { form.patchValue(this.resolveStoreToForm(formName, form, merged.arrControlFactory), { emitEvent: merged.emitEvent }); } else { /** else update the store with the current form state */ this.updateStore(formName, form, true); if (merged.persistForm) { this.storeFormInstance(formName, form); } } this.valueChanges[formName] = merge(form.valueChanges, form.statusChanges.pipe(distinctUntilChanged())) .pipe(debounceTime(merged.debounceTime)) .subscribe(() => this.updateStore(formName, form)); return this; } remove(formName) { if (formName) { this.removeFromStore(formName); } else { const availableForms = Object.keys(this.query.getValue()); for (const name of availableForms) { this.removeFromStore(name); } } this.unsubscribe(formName); } unsubscribe(formName, config = {}) { const _config = Object.assign({ removeNgForm: true }, { updateStore: this._options.updateStoreOnUnsubscribe }, config); const _formName = formName; const removeInstance = (name) => (_config.removeNgForm ? this.removeFormInstance(name) : null); if (_formName) { if (this.valueChanges[_formName]) { this.valueChanges[_formName].unsubscribe(); delete this.valueChanges[_formName]; if (config.updateStore && this.ngForms[_formName]) { this.updateStore(_formName, this.getNgForm(_formName)); } removeInstance(_formName); } } else { for (const name of Object.keys(this.valueChanges)) { this.valueChanges[name].unsubscribe(); if (config.updateStore && this.ngForms[name]) { this.updateStore(name, this.getNgForm(name)); } removeInstance(name); } this.valueChanges = {}; } } removeFromStore(formName) { const snapshot = this.query.getValue(); const newState = Object.keys(snapshot).reduce((acc, currentFormName) => { if (formName !== currentFormName) { acc[currentFormName] = snapshot[currentFormName]; } return acc; }, {}); logAction(`Remove ${formName}`); this.store._setState(() => newState); } resolveControl(form, path) { const [first, ...rest] = path.split('.'); if (rest.length === 0) { return form.controls[first]; } return this.find(form.controls[first], rest); } find(control, path) { return path.reduce((current, name) => { return current.controls.hasOwnProperty(name) ? current.controls[name] : null; }, control); } resolveStoreToForm(formName, control, arrControlFactory) { const form = this.getForm(formName); const value = form.value; /** It means it a single control */ if (!form.controls) { return value; } this.handleFormArray(value, control, arrControlFactory); return value; } handleFormArray(formValue, control, arrControlFactory) { if (control instanceof FormArray) { this.cleanArray(control); if (!arrControlFactory) { throw new Error('Please provide arrControlFactory'); } formValue.forEach((v, i) => control.insert(i, arrControlFactory(v))); } else { Object.keys(formValue).forEach(controlName => { const value = formValue[controlName]; if (Array.isArray(value) && control.get(controlName) instanceof FormArray === true) { if (!arrControlFactory || (arrControlFactory && controlName in arrControlFactory === false)) { throw new Error('Please provide arrControlFactory for ' + controlName); } const current = control.get(controlName); const fc = arrControlFactory[controlName]; this.cleanArray(current); value.forEach((v, i) => current.insert(i, fc(v))); } }); } } cleanArray(control) { while (control.length !== 0) { control.removeAt(0); } } buildFormStoreState(formName, form) { let value; if (form instanceof FormControl) { value = this.resolveFormToStore(form); } if (form instanceof FormGroup || form instanceof FormArray) { // The root form group value = Object.assign({}, this.resolveFormToStore(form), { controls: {} }); for (const key of Object.keys(form.controls)) { const control = form.controls[key]; if (control instanceof FormGroup || form instanceof FormArray) { value.controls[key] = this.buildFormStoreState(formName, control); } else { value.controls[key] = this.resolveFormToStore(control); } } } return value; } updateStore(formName, form, initial = false) { const value = this.buildFormStoreState(formName, form); const capitalized = formName[0].toUpperCase() + formName.slice(1); const action = `${initial ? 'Create' : 'Update'} ${capitalized} Form`; logAction(action); this.store.update({ [formName]: value }); } resolveFormToStore(control) { return { value: this.cloneValue(control.value), rawValue: control.getRawValue ? control.getRawValue() : null, valid: control.valid, dirty: control.dirty, invalid: control.invalid, disabled: control.disabled, errors: control.errors, touched: control.touched, pristine: control.pristine, pending: control.pending }; } cloneValue(value) { return this.isObject(value) ? Object.assign({}, value) : Array.isArray(value) ? [...value] : value; } isObject(val) { if (val == null) { return false; } if (Array.isArray(val)) { return false; } return typeof val === 'function' || typeof val === 'object'; } storeFormInstance(formName, form) { const newForms = Object.assign({}, this.ngForms, { [formName]: form }); this.ngForms = newForms; } removeFormInstance(formName) { if (this.ngForms[formName]) { delete this.ngForms[formName]; } } }; AkitaNgFormsManager.ctorParameters = () => [ { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [FORMS_MANAGER_OPTIONS,] }] } ]; AkitaNgFormsManager.ɵprov = i0.ɵɵdefineInjectable({ factory: function AkitaNgFormsManager_Factory() { return new AkitaNgFormsManager(i0.ɵɵinject(i1.FORMS_MANAGER_OPTIONS, 8)); }, token: AkitaNgFormsManager, providedIn: "root" }); AkitaNgFormsManager = tslib_1.__decorate([ Injectable({ providedIn: 'root' }), tslib_1.__param(0, Optional()), tslib_1.__param(0, Inject(FORMS_MANAGER_OPTIONS)), tslib_1.__metadata("design:paramtypes", [Object]) ], AkitaNgFormsManager); export { AkitaNgFormsManager }; export function setValidators(control, validator) { control.setValidators(coerceArray(validator)); control.updateValueAndValidity(); } export function setAsyncValidators(control, validator) { control.setValidators(coerceArray(validator)); control.updateValueAndValidity(); } //# sourceMappingURL=forms-manager.js.map