@datorama/akita-ng-forms-manager
Version:
The best way to manage your Angular form state in Akita
280 lines • 11.1 kB
JavaScript
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