@ng-flexy/form
Version:
Flexy components and tools to build Angular 8+ applications
1,306 lines (1,295 loc) • 72.9 kB
JavaScript
import * as i0 from '@angular/core';
import { Directive, Renderer2, ElementRef, Input, ViewContainerRef, ComponentFactoryResolver, InjectionToken, Injectable, Optional, Inject, EventEmitter, Component, ChangeDetectorRef, Output, TemplateRef, Pipe, NgModule } from '@angular/core';
import * as jsonata_ from 'jsonata';
import * as i3 from '@angular/forms';
import { FormControl, FormArray, Validators, FormGroup, AbstractControl, FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms';
import * as i2 from '@ng-flexy/layout';
import { FlexyLayout, FlexyLayoutJsonMapperService, FlexyLayoutModule } from '@ng-flexy/layout';
import { uniq, set, merge, has, get, cloneDeep, template } from 'lodash';
import * as i4 from '@ng-flexy/core';
import { FlexyLoggerService } from '@ng-flexy/core';
import { BehaviorSubject } from 'rxjs';
import { skip, debounceTime, map } from 'rxjs/operators';
import { __awaiter } from 'tslib';
import { TranslateService, TranslateModule } from '@ngx-translate/core';
import * as i1 from '@angular/common/http';
import { HttpClient } from '@angular/common/http';
import { CommonModule } from '@angular/common';
const jsonata$4 = jsonata_;
function bindAttributes(schema, nativeEl, renderer, data) {
if (nativeEl && renderer && schema.attributes) {
Object.keys(schema.attributes).forEach(attrKey => {
if (typeof schema.attributes[attrKey] === 'object') {
const attrValues = [];
Object.keys(schema.attributes[attrKey]).forEach(oKey => {
if (schema.attributes[attrKey][oKey]) {
try {
const is = jsonata$4(schema.attributes[attrKey][oKey]).evaluate(data);
if (is) {
attrValues.push(oKey);
}
}
catch (e) {
// do nothing
}
}
});
renderer.setAttribute(nativeEl, attrKey, attrValues.join(' '));
}
else if (typeof schema.attributes[attrKey] === 'string') {
renderer.setAttribute(nativeEl, attrKey, schema.attributes[attrKey]);
}
});
}
}
class FlexyFormAttributesDirective {
constructor(renderer, el) {
this.renderer = renderer;
this.el = el;
}
ngOnInit() {
if (this.componentSchema && this.componentSchema.attributes) {
bindAttributes(this.componentSchema, this.el.nativeElement, this.renderer, this.flexyForm.currentData);
this._changesSubscription = this.flexyForm.currentData$.subscribe(data => {
bindAttributes(this.componentSchema, this.el.nativeElement, this.renderer, data);
});
}
}
ngOnDestroy() {
if (this._changesSubscription) {
this._changesSubscription.unsubscribe();
}
}
}
FlexyFormAttributesDirective.decorators = [
{ type: Directive, args: [{
selector: '[flexyFormAttributes]'
},] }
];
FlexyFormAttributesDirective.ctorParameters = () => [
{ type: Renderer2 },
{ type: ElementRef }
];
FlexyFormAttributesDirective.propDecorators = {
flexyForm: [{ type: Input }],
componentSchema: [{ type: Input }]
};
const LAYOUT_SCHEMA_KEY = 'layoutSchema';
const LAYOUT_FORM_KEY = 'form';
class FlexyFormContainerDirective {
constructor(vc, resolver, renderer) {
this.vc = vc;
this.resolver = resolver;
this.renderer = renderer;
}
set componentSchema(schema) {
this._schema = schema;
}
get componentSchema() {
return this._schema;
}
ngOnInit() {
if (!this._schema) {
return;
}
if (!this._schema.componentType) {
return;
}
const componentFactory = this.resolver.resolveComponentFactory(this._schema.componentType);
const viewContainerRef = this.vc;
viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent(componentFactory);
componentRef.instance[LAYOUT_SCHEMA_KEY] = this._schema;
componentRef.instance[LAYOUT_FORM_KEY] = this.flexyForm;
this._schema.componentRef = componentRef;
if (this._schema.componentInputs) {
Object.keys(this._schema.componentInputs).forEach(key => {
componentRef.instance[key] = this._schema.componentInputs[key];
});
}
this._componentRef = componentRef;
if (this._schema.attributes) {
bindAttributes(this._schema, this._componentRef.location.nativeElement, this.renderer, this.flexyForm.currentData);
}
this._changesSubscription = this.flexyForm.currentData$.subscribe(data => {
if (this._componentRef) {
bindAttributes(this.componentSchema, this._componentRef.location.nativeElement, this.renderer, data);
}
});
}
ngOnDestroy() {
if (this._changesSubscription) {
this._changesSubscription.unsubscribe();
}
}
}
FlexyFormContainerDirective.decorators = [
{ type: Directive, args: [{
selector: '[flexyFormContainer]'
},] }
];
FlexyFormContainerDirective.ctorParameters = () => [
{ type: ViewContainerRef },
{ type: ComponentFactoryResolver },
{ type: Renderer2 }
];
FlexyFormContainerDirective.propDecorators = {
flexyForm: [{ type: Input }],
componentSchema: [{ type: Input }]
};
const COMPLEX_TYPE_INDEX_MARKER = '{%}';
var FlexyFormFieldType;
(function (FlexyFormFieldType) {
FlexyFormFieldType["String"] = "string";
FlexyFormFieldType["Number"] = "number";
FlexyFormFieldType["Boolean"] = "boolean";
FlexyFormFieldType["Array"] = "array";
FlexyFormFieldType["Group"] = "group";
})(FlexyFormFieldType || (FlexyFormFieldType = {}));
var FlexyFormsValidators;
(function (FlexyFormsValidators) {
function notEmptyValidator(control) {
if (!control) {
return null;
}
if (FlexyFormsValidators.isEmpty(control)) {
return {
'not-empty': true
};
}
return null;
}
FlexyFormsValidators.notEmptyValidator = notEmptyValidator;
function noWhitespaceValidator(control) {
if (!control) {
return null;
}
if ((control.value || '').trim().length === 0) {
return { whitespace: true };
}
return null;
}
FlexyFormsValidators.noWhitespaceValidator = noWhitespaceValidator;
function emailValidator(control) {
if (!control) {
return null;
}
const re = new RegExp([
'^(([^<>()\\[\\]\\\\.,;:!#\\s@"]+(\\.[^<>()\\[\\]\\\\.,;:!#\\s@"]+)*)|(".+"))@',
'((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)',
'+[a-zA-Z]{2,})|([a-zA-Z\\-0-9]+))$'
].join(''));
if (!FlexyFormsValidators.isEmpty(control) && !re.test(control.value)) {
return {
'invalid-email': {
currentValue: control.value
}
};
}
return null;
}
FlexyFormsValidators.emailValidator = emailValidator;
function booleanValidator(control) {
if (!control) {
return null;
}
if (!FlexyFormsValidators.isEmpty(control) && typeof control.value !== 'boolean') {
return {
'invalid-boolean': {
currentValue: control.value
}
};
}
return null;
}
FlexyFormsValidators.booleanValidator = booleanValidator;
function integerValidator(control) {
if (!control) {
return null;
}
const re = /^-?\d+$/;
if (!FlexyFormsValidators.isEmpty(control) && !re.test(control.value)) {
return {
'invalid-integer': {
currentValue: control.value
}
};
}
return null;
}
FlexyFormsValidators.integerValidator = integerValidator;
function minValidator(min) {
return (control) => {
if (!control) {
return null;
}
if (!(min || min === 0)) {
return {
'invalid-min': {
wrongConfiguration: true
}
};
}
const notNumber = FlexyFormsValidators.numberValidator(control);
if (notNumber) {
return notNumber;
}
if ((!FlexyFormsValidators.isEmpty(control) || control.value === 0) && control.value < min) {
return {
'invalid-min': {
minimumValue: min,
currentValue: control.value
}
};
}
return null;
};
}
FlexyFormsValidators.minValidator = minValidator;
function maxValidator(max) {
return (control) => {
if (!control) {
return null;
}
if (!(max || max === 0)) {
return {
'invalid-max': {
wrongConfiguration: true
}
};
}
const notNumber = FlexyFormsValidators.numberValidator(control);
if (notNumber) {
return notNumber;
}
if ((!FlexyFormsValidators.isEmpty(control) || control.value === 0) && control.value > max) {
return {
'invalid-max': {
maximumValue: max,
currentValue: control.value
}
};
}
return null;
};
}
FlexyFormsValidators.maxValidator = maxValidator;
function numberValidator(control) {
if (!control) {
return null;
}
const re = /^-?(\d+\.?\d*)$|^(\d*\.?\d+)$/;
if (!FlexyFormsValidators.isEmpty(control) && !re.test(control.value)) {
return {
'invalid-number': {
currentValue: control.value
}
};
}
return null;
}
FlexyFormsValidators.numberValidator = numberValidator;
function minLengthArray(min) {
return (control) => {
if (!control) {
return null;
}
if (!(min || min === 0)) {
return {
'min-length-array': {
wrongConfiguration: true
}
};
}
if (control.value && Array.isArray(control.value) && control.value.length >= min) {
return null;
}
return {
'min-length-array': {
minimumLength: min,
currentLength: control.value
}
};
};
}
FlexyFormsValidators.minLengthArray = minLengthArray;
function maxLengthArray(max) {
return (control) => {
if (!control) {
return null;
}
if (!(max || max === 0)) {
return {
'max-length-array': {
wrongConfiguration: true
}
};
}
if (control.value && control.value.length <= max) {
return null;
}
return {
'max-length-array': {
maximumLength: max,
currentLength: control.value
}
};
};
}
FlexyFormsValidators.maxLengthArray = maxLengthArray;
function isEmpty(control) {
if (!control) {
return null;
}
if (!control.value || (Array.isArray(control.value) && control.value.length === 0)) {
return true;
}
else {
return false;
}
}
FlexyFormsValidators.isEmpty = isEmpty;
function urlValidator(control) {
if (!control) {
return null;
}
const re = new RegExp([
'((([A-Za-z]{3,9}:(?:\\/\\/)?)(?:[\\-;:&=\\+\\$,\\w]+@)?[A-Za-z0-9\\.\\-]',
'+|(?:www\\.|[\\-;:&=\\+\\$,\\w]+@)[A-Za-z0-9\\.\\-]+)((?:\\/[\\+~%\\/\\.\\w\\-_]*)?\\??',
'(?:[\\-\\+=&;%@\\.\\w_]*)#?(?:[\\.\\!\\/\\\\\\w]*))?)'
].join(''));
if (!FlexyFormsValidators.isEmpty(control) && !re.test(control.value)) {
return {
'invalid-url': {
currentValue: control.value
}
};
}
return null;
}
FlexyFormsValidators.urlValidator = urlValidator;
function crossFieldValidator(fields) {
return (control) => {
if (!control) {
return null;
}
if (!(fields && fields.lower && fields.greater)) {
return {
'cross-field-invalid': {
wrongConfiguration: true
}
};
}
const lower = getControl(fields.lower.path, control);
const greater = getControl(fields.greater.path, control);
if (!(greater && greater.valid && lower && lower.valid && greater.value < lower.value)) {
return null;
}
return {
'cross-field-invalid': {
greater: fields.greater.name,
greaterPath: fields.greater.path,
greaterValue: greater && greater.value,
lower: fields.lower.name,
lowerPath: fields.lower.path,
lowerValue: lower && lower.value
}
};
};
}
FlexyFormsValidators.crossFieldValidator = crossFieldValidator;
function crossFieldMinValidator(minPath) {
return (control) => {
if (!control) {
return null;
}
if (!minPath) {
return {
'invalid-min': {
wrongConfiguration: true
}
};
}
const min = getControl(minPath, control);
if (!(min && control.valid && control.value < min.value)) {
return null;
}
return {
'invalid-min': {
minimumValue: min.value,
currentValue: control.value
}
};
};
}
FlexyFormsValidators.crossFieldMinValidator = crossFieldMinValidator;
function crossFieldMaxValidator(maxPath) {
return (control) => {
if (!control) {
return null;
}
if (!maxPath) {
return {
'invalid-max': {
wrongConfiguration: true
}
};
}
const max = getControl(maxPath, control);
if (!(max && control.valid && control.value > max.value)) {
return null;
}
return {
'invalid-max': {
maximumValue: max.value,
currentValue: control.value
}
};
};
}
FlexyFormsValidators.crossFieldMaxValidator = crossFieldMaxValidator;
function crossFieldAbsoluteMinValidator(minPath) {
return (control) => {
if (!control) {
return null;
}
if (!minPath) {
return {
'absolute-min-invalid': {
wrongConfiguration: true
}
};
}
const min = getControl(minPath, control);
if (min && control.valid && min.valid && Math.abs(control.value) >= min.value) {
return null;
}
return {
'absolute-min-invalid': {
min: min.value,
currentValue: control.value
}
};
};
}
FlexyFormsValidators.crossFieldAbsoluteMinValidator = crossFieldAbsoluteMinValidator;
function forbiddenValuesValidator(forbiddenValues) {
return (control) => {
if (!control) {
return null;
}
if (!forbiddenValues) {
return {
'forbidden-value': {
wrongConfiguration: true
}
};
}
if (!forbiddenValues.includes(control.value)) {
return null;
}
return {
'forbidden-value': {
value: control.value
}
};
};
}
FlexyFormsValidators.forbiddenValuesValidator = forbiddenValuesValidator;
function arrayUniqueFieldsValidator(data) {
return (control) => {
if (!control || !control.controls) {
return null;
}
if (!(data && data.path)) {
return {
'value-duplicate': {
wrongConfiguration: true
}
};
}
const comparedValues = [];
control.controls.forEach(item => {
const compared = getControl(data.path, item);
if (compared) {
comparedValues.push(compared.value);
}
});
if (uniq(comparedValues).length === comparedValues.length) {
return null;
}
return {
'value-duplicate': {
field: data.fieldName
}
};
};
}
FlexyFormsValidators.arrayUniqueFieldsValidator = arrayUniqueFieldsValidator;
function getControl(path, control) {
let arrayPath = [];
if (Array.isArray(path)) {
arrayPath = path;
}
else if (path) {
const parents = ('' + path).indexOf('../') !== -1 ? path.split('../') : [path];
const sPath = '' + parents.pop();
const stringPath = sPath.split('.').map(i => {
if (i.match(/^[0-9]+$/)) {
return parseInt(i, 10);
}
else {
return i;
}
});
parents.fill('../');
parents.push(...stringPath);
arrayPath = parents;
}
arrayPath.forEach(i => {
if (control && control.parent && i === '../') {
control = control.parent;
}
else if (control && control.controls) {
control = Number.isInteger(i) ? control.get(Object.keys(control.controls)[i]) : control.controls[i];
}
});
return control;
}
})(FlexyFormsValidators || (FlexyFormsValidators = {}));
const HIDDEN_IF_GROUP_NAME = '__if__';
const HIDDEN_CALC_GROUP_NAME = '__calc__';
function parseFormJson(json) {
if (Array.isArray(json)) {
return parseFormVersion1(json);
}
else if (json.schemaVersion === 1) {
return parseFormVersion1(json.schema);
}
else {
const schema = json.schema;
assignHiddenNames(schema);
checkSchema(schema);
return json.schema;
}
}
function checkSchema(schema) {
if (schema && Array.isArray(schema)) {
schema.forEach((jsonItem, index) => {
if (jsonItem.if &&
(jsonItem.name || jsonItem.type)) {
console.warn('Wrong if schema', jsonItem);
}
if (jsonItem.children) {
checkSchema(jsonItem.children);
}
});
}
}
function assignHiddenNames(schema) {
if (schema && Array.isArray(schema)) {
schema.forEach((jsonItem, index) => {
// if ((jsonItem as FlexyFormIfJsonSchema).if && !(jsonItem as FlexyFormFieldLayoutJsonSchema).type) {
// (jsonItem as FlexyFormFieldLayoutJsonSchema).type = FlexyFormFieldType.Group;
// }
if (jsonItem.calc && !jsonItem.name) {
jsonItem.name =
HIDDEN_CALC_GROUP_NAME +
'.' +
(jsonItem.id
? jsonItem.id
: 'ui-' +
Math.random()
.toString(36)
.substr(2, 9));
}
if (jsonItem.children) {
assignHiddenNames(jsonItem.children);
}
});
}
}
function parseFormVersion1(json) {
const parsed = [];
json.forEach(item => {
parsed.push(parseFormVersion1Item(item));
});
return parsed;
}
function parseFormVersion1Item(item) {
const schema = {};
if (item.properties && item.properties.class) {
if (!schema.attributes) {
schema.attributes = {};
}
schema.attributes.class = item.properties.class;
}
if (item.component) {
Object.assign(schema, {
component: item.component,
properties: item.componentInputs ? item.componentInputs : {}
});
}
if (item.controlGroupName) {
Object.assign(schema, {
name: item.controlGroupName,
type: FlexyFormFieldType.Group,
validators: item.validators,
groupKey: item.groupKey,
items: item.items ? parseFormVersion1Item(item.items) : void 0,
indexDef: item.itemKeyDef,
indexPattern: item.itemKeyPattern,
indexGenPattern: item.itemKeyGen
});
}
else if (item.controlArrayName) {
Object.assign(schema, {
name: item.controlArrayName,
type: FlexyFormFieldType.Array,
validators: item.validators,
items: item.items ? parseFormVersion1Item(item.items) : void 0,
indexDef: item.itemIndexDef
});
}
else if (item.controlName) {
Object.assign(schema, {
name: item.controlName,
validators: item.validators
});
}
if (item.children) {
schema.children = parseFormVersion1(item.children);
}
return schema;
}
function replaceMarker(s, marker, key) {
return s.split(marker).join('' + key);
}
const jsonata$3 = jsonata_;
const ifExpressionsCache = {};
const calculatedExpresionCache = {};
var FlexyFormDataMode;
(function (FlexyFormDataMode) {
FlexyFormDataMode["All"] = "all";
FlexyFormDataMode["Dirty"] = "dirty";
FlexyFormDataMode["Touched"] = "toched";
})(FlexyFormDataMode || (FlexyFormDataMode = {}));
function findErrors(schema, currentData) {
const errors = {};
for (const item of schema) {
if (checkIf(item, currentData) && item.items) {
item.items.forEach((aItem, index) => {
const arrItemError = findErrors([aItem], currentData);
if (arrItemError && Object.values(arrItemError).length) {
errors[item.formName + '.' + index] = arrItemError;
}
});
}
else if (checkIf(item, currentData) &&
item.formName &&
item.formControl &&
item.formControl.invalid) {
errors[item.formName] = item.formControl.errors;
}
if (checkIf(item, currentData) && item.children) {
Object.assign(errors, findErrors(item.children, currentData));
}
}
return errors;
}
function findSchema(fieldName, schema) {
for (const item of schema) {
if (item.formName && item.formName === fieldName) {
return item;
}
else if (item.children) {
const childSchema = findSchema(fieldName, item.children);
if (childSchema) {
return childSchema;
}
}
}
return null;
}
function calculate(calcExp, data) {
let value;
try {
if (!calculatedExpresionCache[calcExp]) {
calculatedExpresionCache[calcExp] = jsonata$3(calcExp);
}
value = calculatedExpresionCache[calcExp].evaluate(data);
}
catch (e) {
// console.error(e);
value = null;
}
return value;
}
function getSchemaData$1(schemas, currentData, mode = FlexyFormDataMode.All) {
let data = {};
if (schemas) {
schemas.forEach(schema => {
const fieldSchema = schema;
if (checkIf(fieldSchema, currentData)) {
const isFormControl = fieldSchema.formControl && fieldSchema.formName;
if (isFormControl && fieldSchema.formControl instanceof FormControl) {
if (checkSchemaData(fieldSchema.formControl, mode)) {
set(data, fieldSchema.formName, fieldSchema.formControl.value);
}
}
else if (isFormControl && fieldSchema.formControl instanceof FormArray) {
const arrayData = getArrayData(fieldSchema, currentData, mode, data);
if (mode === FlexyFormDataMode.All) {
set(data, fieldSchema.formName, Object.values(arrayData));
}
else if (!isInputEmpty(arrayData)) {
set(data, fieldSchema.formName, arrayData);
}
}
if (checkIf(fieldSchema, currentData)) {
data = merge(data, getSchemaData$1(fieldSchema.children, currentData, mode));
}
}
});
}
return data;
}
function findRemoved(allData, originalData) {
const removed = {};
if (originalData) {
Object.keys(originalData).forEach(key => {
const path = key;
if (!isInputEmpty(originalData[key]) && isInputEmpty(allData[key])) {
set(removed, path, null);
}
else if (originalData[key] && Array.isArray(originalData[key])) {
originalData[key].forEach((item, index) => {
if (!isInputEmpty(item) && isInputEmpty(allData[key][index])) {
if (!has(removed, path)) {
set(removed, path, {});
}
const v = get(removed, path);
v['' + index] = null;
}
else if (item && isObject(item)) {
const founded = findRemoved(allData[key][index], item);
if (founded && !isInputEmpty(founded)) {
if (!has(removed, path)) {
set(removed, path, {});
}
const v = get(removed, path);
v['' + index] = founded;
}
}
});
}
else if (originalData[key] && isObject(originalData[key])) {
const founded = findRemoved(allData[key], originalData[key]);
if (founded && !isInputEmpty(founded)) {
set(removed, path, founded);
}
}
});
}
return removed;
}
function clearEmptyArrayAndObjects(data) {
if (data) {
if (isObject(data)) {
Object.keys(data).forEach(key => {
if (isEmptyStructure(data[key])) {
delete data[key];
}
else if (isObject(data[key])) {
clearEmptyArrayAndObjects(data[key]);
}
});
}
}
}
function isObject(a) {
return !!a && a.constructor === Object;
}
function checkIf(fieldSchema, currentData) {
if (!fieldSchema.if) {
return true;
}
const ifName = 'IF_' + fieldSchema.if;
let ret;
try {
if (!ifExpressionsCache[ifName]) {
ifExpressionsCache[ifName] = jsonata$3(fieldSchema.if);
}
ret = ifExpressionsCache[ifName].evaluate(currentData ? currentData : {});
}
catch (e) {
// console.error(e);
ret = null;
}
return !!ret;
}
function getArrayData(fieldSchema, currentData, mode, data) {
const arrayData = {};
fieldSchema.items.forEach((item, index) => {
const itemFormControl = item.formControl;
if (!itemFormControl || checkSchemaData(itemFormControl, mode)) {
if (item.children) {
const itemData = getSchemaData$1(item.children, currentData, mode);
if (!isInputEmpty(itemData)) {
arrayData['' + index] = itemData;
}
}
else {
arrayData['' + index] = item.formControl.value;
}
}
});
return arrayData;
}
function checkSchemaData(control, mode) {
return (control &&
(mode === FlexyFormDataMode.All ||
(mode === FlexyFormDataMode.Dirty && control.dirty) ||
(mode === FlexyFormDataMode.Touched && control.touched)));
}
function isInputEmpty(v) {
return v === void 0 || v === '';
}
function isEmptyStructure(data) {
let ret = true;
if (Array.isArray(data)) {
if (data.length > 0) {
data.forEach(item => {
ret = ret && isEmptyStructure(item);
});
}
}
else if (isObject(data)) {
if (Object.keys(data).length > 0) {
Object.keys(data).forEach(key => {
ret = ret && isEmptyStructure(data[key]);
});
}
}
else {
return isInputEmpty(data);
}
return ret;
}
class FlexyForm extends FlexyLayout {
constructor(formGroup, schema, data) {
super(schema);
this.isStarted = false;
this._calculatedRefs = {};
this._currentDataSubject = new BehaviorSubject(data);
this.currentData$ = this._currentDataSubject.asObservable();
this.formGroup = formGroup;
this.schema = schema;
this._initCalculatedRefs(schema);
this._originalData = cloneDeep(data);
this._setCurrentData();
// refresh attributes
this._setCurrentData();
// jump to next tick
// setTimeout(() => {
this._subscribeChangesAndCalculate();
// });
}
get valid() {
return !this.getAllErrors();
}
getAllData() {
const data = cloneDeep(getSchemaData$1(this.schema, this.currentData));
this._clearHiddenData(data);
return data;
}
// @deprecated
getDirtyData() {
const data = cloneDeep(getSchemaData$1(this.schema, this.currentData, FlexyFormDataMode.Dirty));
this._clearHiddenData(data);
clearEmptyArrayAndObjects(data);
const allData = this.getAllData();
const removed = findRemoved(allData, this._originalData);
clearEmptyArrayAndObjects(removed);
return merge(data, removed);
}
getAllErrors() {
return this._lastErrors;
}
containsFieldSchema(fieldName) {
return !!findSchema(fieldName, this.schema);
}
getFieldSchema(fieldName) {
return findSchema(fieldName, this.schema);
}
getFieldInstance(fieldName) {
const schema = findSchema(fieldName, this.schema);
if (schema && schema.componentRef) {
return schema.componentRef.instance;
}
return null;
}
_subscribeChangesAndCalculate() {
this._setCurrentData();
this.isStarted = true;
// this._setCurrentData();
this._changesSubscription = this.formGroup.valueChanges.subscribe(data => {
const hash = this.currentDataHash;
this._setCurrentData();
if (hash !== this.currentDataHash) {
this._currentDataSubject.next(this.currentData);
}
});
this._currentDataSubject.next(this.currentData);
}
_setCurrentData() {
this.currentData = getSchemaData$1(this.schema, this.currentData);
this.currentDataHash = JSON.stringify(this.currentData);
this._calculate();
this.currentData = getSchemaData$1(this.schema, this.currentData);
this.currentDataHash = JSON.stringify(this.currentData);
const errors = findErrors(this.schema, this.currentData);
this._lastErrors = errors && Object.keys(errors).length ? errors : null;
}
_initCalculatedRefs(schema) {
if (schema) {
schema.forEach((schemaItem) => {
if (schemaItem.formName && schemaItem.formControl && schemaItem.calc) {
this._calculatedRefs[schemaItem.formName] = {
calc: schemaItem.calc,
control: schemaItem.formControl
};
}
if (schemaItem.children) {
this._initCalculatedRefs(schemaItem.children);
}
});
}
}
_calculate() {
if (this._calculatedRefs) {
Object.values(this._calculatedRefs).forEach(calc => {
const value = calculate(calc.calc, this.currentData);
if (value !== calc.control.value) {
calc.control.setValue(value);
calc.control.markAsDirty();
}
});
}
}
_clearHiddenData(data) {
if (data[HIDDEN_CALC_GROUP_NAME]) {
delete data[HIDDEN_CALC_GROUP_NAME];
}
}
}
const FLEXY_FORM_VALIDATORS = new InjectionToken('FLEXY_FORM_VALIDATORS');
const INPUTS_READONLY_KEY = 'readonly';
const SCHEMA_CONTROL_NAME_KEY = 'name';
const SCHEMA_GROUP_KEY = 'groupKey';
const SCHEMA_COMPONENT_INPUTS_KEY = 'properties';
const SCHEMA_DEFAULT_KEY = 'default';
const ɵ0 = () => Validators.required, ɵ1 = data => Validators.maxLength(data), ɵ2 = data => Validators.minLength(data), ɵ3 = data => FlexyFormsValidators.minValidator(data), ɵ4 = data => FlexyFormsValidators.maxValidator(data), ɵ5 = () => FlexyFormsValidators.numberValidator, ɵ6 = () => FlexyFormsValidators.integerValidator, ɵ7 = () => FlexyFormsValidators.booleanValidator, ɵ8 = () => FlexyFormsValidators.emailValidator, ɵ9 = () => FlexyFormsValidators.noWhitespaceValidator, ɵ10 = () => FlexyFormsValidators.notEmptyValidator, ɵ11 = data => Validators.pattern(data), ɵ12 = data => FlexyFormsValidators.crossFieldValidator(data), ɵ13 = data => FlexyFormsValidators.crossFieldMinValidator(data), ɵ14 = data => FlexyFormsValidators.crossFieldMaxValidator(data), ɵ15 = data => FlexyFormsValidators.crossFieldAbsoluteMinValidator(data), ɵ16 = data => FlexyFormsValidators.forbiddenValuesValidator(data), ɵ17 = data => FlexyFormsValidators.arrayUniqueFieldsValidator(data), ɵ18 = data => FlexyFormsValidators.minLengthArray(data), ɵ19 = data => FlexyFormsValidators.maxLengthArray(data);
const DEFAULT_VALIDATORS_MAP = {
required: ɵ0,
maxLength: ɵ1,
minLength: ɵ2,
min: ɵ3,
max: ɵ4,
number: ɵ5,
integer: ɵ6,
boolean: ɵ7,
email: ɵ8,
noWhitespace: ɵ9,
notEmpty: ɵ10,
pattern: ɵ11,
crossField: ɵ12,
crossFieldMin: ɵ13,
crossFieldMax: ɵ14,
crossFieldAbsoluteMin: ɵ15,
forbiddenValues: ɵ16,
arrayUniqueFields: ɵ17,
minItems: ɵ18,
maxItems: ɵ19
};
class FlexyFormJsonMapperService {
constructor(validatorsMap, jsonLayoutMapper, formBuilder, logger) {
this.jsonLayoutMapper = jsonLayoutMapper;
this.formBuilder = formBuilder;
this.logger = logger;
this._validatorsMap = DEFAULT_VALIDATORS_MAP;
if (validatorsMap) {
Object.assign(this._validatorsMap, validatorsMap);
}
}
get supportedValidators() {
return Object.keys(this._validatorsMap);
}
createForm(json, readonlyMode = false, formData) {
this.logger.debug('createForm');
FlexyFormJsonMapperService.controlCounter = 0;
const jsonSchema = parseFormJson(json);
const rootFormGroup = new FormGroup({});
const dynamicSchema = this.map(jsonSchema, readonlyMode, formData, rootFormGroup);
this.logger.debug('countOfControls', FlexyFormJsonMapperService.controlCounter);
return new FlexyForm(rootFormGroup, dynamicSchema, formData);
}
createItemControl(itemsSchema, readonlyMode, value) {
let control;
if (itemsSchema.children) {
control = new FormGroup({});
this.mapItem(itemsSchema, readonlyMode, value, control);
}
else {
control = this.formBuilder.control(value ? value : get(itemsSchema, 'componentInputs.default'), itemsSchema.validators
? this.mapValidators(itemsSchema.validators)
: []);
}
return control;
}
createArrayItemSchema(control, items, itemKeyDef, parentName, readonlyMode, formData, value, index, parentSchema = null) {
const citems = cloneDeep(items);
this.populateComplexTypeIndexMarker([citems], index + 1, value, itemKeyDef);
const isComplex = citems && !!citems.children;
let schema;
if (isComplex) {
const withRootValues = Object.assign({}, value);
const groupSchema = this._jsonLayoutItemMap({}, '' + index, parentSchema);
groupSchema.children = this.map([citems], readonlyMode, withRootValues, control, groupSchema);
schema = groupSchema;
}
else {
schema = this.jsonLayoutMapper.map([citems])[0];
schema.formControl = control;
control.setValue(value);
schema.id = parentSchema.id + ':' + index;
if (!schema.componentInputs) {
schema.componentInputs = {};
}
schema.componentInputs[INPUTS_READONLY_KEY] = readonlyMode;
}
return schema;
}
createGroupItemSchema(control, items, itemKeyDef, parentName, readonlyMode, formData, value, key, parentSchema = null) {
const citems = cloneDeep(items);
this.populateComplexTypeIndexMarker([citems], key, value, itemKeyDef);
const isComplex = citems && !!citems.children;
let schema;
if (isComplex) {
const withRootValues = Object.assign({}, value);
const groupSchema = this.map([citems], readonlyMode, withRootValues, control, null)[0];
schema = groupSchema;
}
else {
schema = this.jsonLayoutMapper.map([citems])[0];
schema.formControl = control;
control.setValue(value);
if (!schema.componentInputs) {
schema.componentInputs = {};
}
schema.componentInputs[INPUTS_READONLY_KEY] = readonlyMode;
schema.formName = citems.name;
}
schema.id = (parentSchema && parentSchema.id ? parentSchema.id + ':' : '') + key;
schema.groupKey = key;
return schema;
}
createSchema(json, readonlyMode = false, formData = {}, parentFormGroup, parentControlGroupName, parentSchema = null) {
const schema = [];
if (json && Array.isArray(json)) {
json.forEach((jsonItem, index) => {
const schemaItem = this.mapItem(jsonItem, readonlyMode, formData, parentFormGroup, parentControlGroupName, parentSchema, '' + index);
let itemParentFormGroup = parentFormGroup;
let itemParentControlGroupName = parentControlGroupName;
if (schemaItem.formControl instanceof FormGroup) {
itemParentFormGroup = schemaItem.formControl;
if ([FlexyFormFieldType.Group, FlexyFormFieldType.Array].includes(jsonItem.type)) {
itemParentControlGroupName = this.controlComplexName(jsonItem, itemParentControlGroupName);
}
}
if (jsonItem.children) {
schemaItem.children = this.createSchema(jsonItem.children, readonlyMode, formData, itemParentFormGroup, itemParentControlGroupName, schemaItem);
}
schema.push(schemaItem);
});
}
return schema;
}
map(json, readonlyMode = false, formData, parentFormGroup, parentSchema = null) {
const dynamicSchema = this.createSchema(json, readonlyMode, formData, parentFormGroup, null, parentSchema);
return dynamicSchema;
}
createControl(config) {
return config instanceof AbstractControl
? config
: config[1]
? this.formBuilder.control(config[0], config[1])
: this.formBuilder.control(config);
}
controlComplexName(jsonItem, parentName) {
return (parentName && jsonItem.name[0] === '.' ? parentName : '') + jsonItem.name;
}
mapItem(jsonItem, readonlyMode = false, formData = {}, parentFormGroup, parentControlGroupName = null, parentSchema = null, schemaId = '') {
const formSchemaItem = this._jsonLayoutItemMap(jsonItem, schemaId, parentSchema);
if (readonlyMode) {
if (!formSchemaItem.componentInputs) {
formSchemaItem.componentInputs = {};
}
formSchemaItem.componentInputs[INPUTS_READONLY_KEY] = readonlyMode;
}
let controlName = '';
if (jsonItem.name &&
jsonItem.type !== FlexyFormFieldType.Group &&
jsonItem.type !== FlexyFormFieldType.Array) {
controlName = jsonItem.name;
this.mapItemSetFieldControl(formSchemaItem, jsonItem, parentControlGroupName, formData);
}
else if (jsonItem.items &&
jsonItem.type === FlexyFormFieldType.Group) {
const groupJsonItem = jsonItem;
controlName = groupJsonItem.name;
this.mapItemSetGroupControl(formSchemaItem, jsonItem, parentControlGroupName, readonlyMode);
const formGroupName = (parentControlGroupName && groupJsonItem.name[0] === '.' ? parentControlGroupName : '') + groupJsonItem.name;
const formGroupData = has(formData, formGroupName) ? get(formData, formGroupName) : void 0;
if (formGroupData) {
jsonItem.children = [];
const isComplex = !!groupJsonItem.items.children;
Object.keys(formGroupData).forEach(key => {
const schemaJson = cloneDeep(groupJsonItem.items);
schemaJson.name = '.' + key;
if (isComplex) {
schemaJson.type = FlexyFormFieldType.Group;
}
schemaJson[SCHEMA_GROUP_KEY] = key;
this.populateComplexTypeIndexMarker([schemaJson], key, formGroupData[key], groupJsonItem.indexDef);
groupJsonItem.children.push(schemaJson);
});
}
}
else if (jsonItem.items &&
jsonItem.type === FlexyFormFieldType.Array) {
const arrayJsonItem = jsonItem;
controlName = arrayJsonItem.name;
this.mapItemSetArrayControl(formSchemaItem, arrayJsonItem, parentControlGroupName, formData, readonlyMode);
}
else {
controlName =
jsonItem.type === FlexyFormFieldType.Group
? jsonItem.name
: formSchemaItem.id
? 'g-' + formSchemaItem.id
: 'd-' + Date.now();
formSchemaItem.groupKey = jsonItem.groupKey;
// TODO to think: form control is required also for non FlexyFormFieldLayoutSchema
formSchemaItem.formControl = new FormGroup({}, this.mapValidators(jsonItem.validators));
}
const control = formSchemaItem.formControl;
if (control) {
parentFormGroup.addControl(this.unifyName(controlName, parentControlGroupName ? this.unifyName(parentControlGroupName) : ''), control);
}
return formSchemaItem;
}
_jsonLayoutItemMap(jsonItem, schemaId, parentSchema) {
const schema = this.jsonLayoutMapper.mapItem(jsonItem, schemaId, parentSchema);
// const SCHEMA_GROUP_KEY = 'groupKey';
const SCHEMA_IF = 'if';
const SCHEMA_CALC = 'calc';
// TODO tothink is problem with populate form group controls from external domain
[SCHEMA_GROUP_KEY, SCHEMA_IF, SCHEMA_CALC].forEach(key => {
if (jsonItem[key]) {
schema[key] = jsonItem[key];
}
});
return schema;
}
mapItemSetArrayControl(formSchemaItem, jsonItem, parentControlGroupName, formData, readonlyMode) {
let formName = (parentControlGroupName && jsonItem.name[0] === '.' ? parentControlGroupName : '') + jsonItem.name;
// its possible in array in group
if (formName.endsWith('.')) {
formName = formName.slice(0, -1);
}
const val = has(formData, formName) ? get(formData, formName) : void 0;
const formControl = this.createArrayControl(jsonItem, readonlyMode, val);
formSchemaItem.formControl = formControl;
formSchemaItem.formName = formName;
if (jsonItem.items) {
formSchemaItem.items = this.createArrayItems(jsonItem.items, jsonItem.indexDef, formControl, formName, readonlyMode, val, formData, formSchemaItem);
formSchemaItem.componentInputs = Object.assign(Object.assign({}, formSchemaItem.componentInputs), {
jsonSchema: jsonItem,
readonly: readonlyMode
});
}
}
mapItemSetGroupControl(formSchemaItem, jsonItem, parentControlGroupName, readonlyMode) {
const formGroupName = (parentControlGroupName && jsonItem.name[0] === '.' ? parentControlGroupName : '') + jsonItem.name;
formSchemaItem.formControl = new FormGroup({});
if (jsonItem.items) {
formSchemaItem.componentInputs = Object.assign(Object.assign({}, formSchemaItem.componentInputs), {
jsonSchema: jsonItem,
parentGroupName: formGroupName,
readonly: readonlyMode
});
}
}
mapItemSetFieldControl(formSchemaItem, jsonItem, parentControlGroupName, formData) {
FlexyFormJsonMapperService.controlCounter++;
const formName = (parentControlGroupName && jsonItem.name[0] === '.' ? parentControlGroupName : '') + jsonItem.name;
const val = has(formData, formName) ? get(formData, formName) : void 0;
const formControl = this.createControl(this.createControlConfig(jsonItem, val));
formSchemaItem.formName = formName;
formSchemaItem.formControl = formControl;
}
createArrayItems(items, itemKeyDef, arrayControl, parentName, readonlyMode = false, values, formData, parentSchema = null) {
const formArray = arrayControl;
const formSchema = [];
formArray.controls.forEach((control, index) => {
let value;
let key;
if (values && Array.isArray(values)) {
value = values[index];
key = '' + index;
}
else if (values && typeof values === 'object') {
key = Object.keys(values)[index];
value = values[key];
}
const schema = this.createArrayItemSchema(control, items, itemKeyDef, parentName, readonlyMode, formData, value, index, parentSchema);
schema.formName = key;
formSchema.push(schema);
});
return formSchema;
}
populateComplexTypeIndexMarker(items, key, value, marker = COMPLEX_TYPE_INDEX_MARKER) {
items.forEach(item => {
if (item.name) {
item.name = replaceMarker(item.name, marker, key);
}
if (item.if) {
item.if = replaceMarker(item.if, marker, key);
}
if (item.calc) {
item.calc = replaceMarker(item.calc, marker, key);
}
if (item.attributes) {
Object.keys(item.attributes).forEach(attr => {
if (typeof item.attributes[attr] === 'string') {
item.attributes[attr] = replaceMarker('' + item.attributes[attr], marker, key);
}
else if (typeof item.attributes[attr] === 'object') {
Object.keys(item.attributes[attr]).forEach(attrVal => {
const replaced = replaceMarker(attrVal, marker, key);
item.attributes[attr][attrVal] = replaceMarker('' + item.attributes[attr][attrVal], marker, key);
if (replaced !== attrVal) {
item.attributes[attr][replaced] = item.attributes[attr][attrVal];
delete item.attributes[attr][attrVal];
}
});
}
});
}
if (item[SCHEMA_COMPONENT_INPUTS_KEY]) {
const componentInputs = ['title', 'label', 'legend'];
componentInputs.forEach(propName => {
if (item[SCHEMA_COMPONENT_INPUTS_KEY][propName]) {
item[SCHEMA_COMPONENT_INPUTS_KEY][propName] = replaceMarker(item[SCHEMA_COMPONENT_INPUTS_KEY][propName], marker, key);
}