@ng-flexy/form
Version:
Flexy components and tools to build Angular 8+ applications
2,022 lines • 81.9 kB
JavaScript
import * as i0 from '@angular/core';
import { Input, Directive, InjectionToken, Optional, Inject, Injectable, Component, EventEmitter, Output, Pipe, NgModule } from '@angular/core';
import * as jsonata_ from 'jsonata';
import { uniq, set, merge, has, get, cloneDeep, template } from 'lodash';
import { skip, debounceTime, map } from 'rxjs/operators';
import * as i2 from '@angular/forms';
import { FormControl, FormArray, Validators, FormGroup, AbstractControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import * as i1 from '@ng-flexy/layout';
import { FlexyLayout, FlexyLayoutModule } from '@ng-flexy/layout';
import { BehaviorSubject } from 'rxjs';
import * as i2$1 from '@ng-flexy/core';
import * as i1$1 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i1$2 from '@ngx-translate/core';
import { TranslateModule } from '@ngx-translate/core';
import * as i1$3 from '@angular/common/http';
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 {
renderer;
el;
flexyForm;
componentSchema;
_changesSubscription;
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();
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormAttributesDirective, deps: [{ token: i0.Renderer2 }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.15", type: FlexyFormAttributesDirective, isStandalone: false, selector: "[flexyFormAttributes]", inputs: { flexyForm: "flexyForm", componentSchema: "componentSchema" }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormAttributesDirective, decorators: [{
type: Directive,
args: [{
selector: '[flexyFormAttributes]',
standalone: false
}]
}], ctorParameters: () => [{ type: i0.Renderer2 }, { type: i0.ElementRef }], propDecorators: { flexyForm: [{
type: Input
}], componentSchema: [{
type: Input
}] } });
const LAYOUT_SCHEMA_KEY = 'layoutSchema';
const LAYOUT_FORM_KEY = 'form';
class FlexyFormContainerDirective {
vc;
resolver;
renderer;
flexyForm;
set componentSchema(schema) {
this._schema = schema;
}
get componentSchema() {
return this._schema;
}
_schema;
_changesSubscription;
_componentRef;
constructor(vc, resolver, renderer) {
this.vc = vc;
this.resolver = resolver;
this.renderer = renderer;
}
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();
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormContainerDirective, deps: [{ token: i0.ViewContainerRef }, { token: i0.ComponentFactoryResolver }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.15", type: FlexyFormContainerDirective, isStandalone: false, selector: "[flexyFormContainer]", inputs: { flexyForm: "flexyForm", componentSchema: "componentSchema" }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormContainerDirective, decorators: [{
type: Directive,
args: [{
selector: '[flexyFormContainer]',
standalone: false
}]
}], ctorParameters: () => [{ type: i0.ViewContainerRef }, { type: i0.ComponentFactoryResolver }, { type: i0.Renderer2 }], 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 {
currentData;
currentDataHash;
currentData$;
get valid() {
return !this.getAllErrors();
}
isStarted = false;
formGroup;
_originalData;
_currentDataSubject;
_calculatedRefs = {};
_lastErrors;
_changesSubscription;
constructor(formGroup, schema, data) {
super(schema);
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();
// });
}
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 DEFAULT_VALIDATORS_MAP = {
required: () => Validators.required,
maxLength: data => Validators.maxLength(data),
minLength: data => Validators.minLength(data),
min: data => FlexyFormsValidators.minValidator(data),
max: data => FlexyFormsValidators.maxValidator(data),
number: () => FlexyFormsValidators.numberValidator,
integer: () => FlexyFormsValidators.integerValidator,
boolean: () => FlexyFormsValidators.booleanValidator,
email: () => FlexyFormsValidators.emailValidator,
noWhitespace: () => FlexyFormsValidators.noWhitespaceValidator,
notEmpty: () => FlexyFormsValidators.notEmptyValidator,
pattern: data => Validators.pattern(data),
crossField: data => FlexyFormsValidators.crossFieldValidator(data),
crossFieldMin: data => FlexyFormsValidators.crossFieldMinValidator(data),
crossFieldMax: data => FlexyFormsValidators.crossFieldMaxValidator(data),
crossFieldAbsoluteMin: data => FlexyFormsValidators.crossFieldAbsoluteMinValidator(data),
forbiddenValues: data => FlexyFormsValidators.forbiddenValuesValidator(data),
arrayUniqueFields: data => FlexyFormsValidators.arrayUniqueFieldsValidator(data),
minItems: data => FlexyFormsValidators.minLengthArray(data),
maxItems: data => FlexyFormsValidators.maxLengthArray(data)
};
class FlexyFormJsonMapperService {
jsonLayoutMapper;
formBuilder;
logger;
static controlCounter = 0;
get supportedValidators() {
return Object.keys(this._validatorsMap);
}
_validatorsMap = DEFAULT_VALIDATORS_MAP;
constructor(validatorsMap, jsonLayoutMapper, formBuilder, logger) {
this.jsonLayoutMapper = jsonLayoutMapper;
this.formBuilder = formBuilder;
this.logger = logger;
if (validatorsMap) {
Object.assign(this._validatorsMap, 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 = { ...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 = { ...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 = {
...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 = {
...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);
}
});
}
if (item.children) {
this.populateComplexTypeIndexMarker(item.children, key, value, marker);
}
if (item.items) {
this.populateComplexTypeIndexMarker([item.items], key, value, marker);
}
});
}
createControlConfig(item, defaultValue) {
if (item.name) {
return [defaultValue !== void 0 ? defaultValue : item[SCHEMA_DEFAULT_KEY], this.mapValidators(item.validators)];
}
return [];
}
createArrayControl(item, readonlyMode = false, values = []) {
const arraySchema = item;
const validators = [];
// TODO to thkink
// if (arraySchema.validators['minItems']) {
// validators.push(FlexyFormsValidators.minLengthArray(arraySchema.validators['minItems']));
// }
// if (arraySchema.validators['maxItems']) {
// validators.push(FlexyFormsValidators.maxLengthArray(arraySchema.validators['maxItems']));
// }
const formArray = new FormArray([], this.mapValidators(item.validators));
if (item.type === FlexyFormFieldType.Array && item.name) {
if (values && Array.isArray(values)) {
values.forEach(value => {
formArray.push(this.createItemControl(arraySchema.items, readonlyMode, value));
});
}
else if (values) {
console.warn('Wrong type of data. Values should be array', values);
}
}
return formArray;
}
unifyName(controlName, parentControlName = '') {
if (controlName[0] === '.') {
controlName = controlName.substring(1);
}
let name = controlName ? controlName.split('.').join('/') : 'p_' + Date.now();
if (parentControlName) {
name = name.replace(parentControlName + '/', '');
}
return name;
}
mapValidators(jsonValidator) {
const validators = [];
if (jsonValidator) {
Object.keys(jsonValidator).forEach(key => {
if (this._validatorsMap[key]) {
validators.push(this._validatorsMap[key](jsonValidator[key]));
}
else {
console.warn(`Validator ${key} is not supported`);
}
});
}
return validators;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormJsonMapperService, deps: [{ token: FLEXY_FORM_VALIDATORS, optional: true }, { token: i1.FlexyLayoutJsonMapperService }, { token: i2.FormBuilder }, { token: i2$1.FlexyLoggerService }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormJsonMapperService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormJsonMapperService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [FLEXY_FORM_VALIDATORS]
}] }, { type: i1.FlexyLayoutJsonMapperService }, { type: i2.FormBuilder }, { type: i2$1.FlexyLoggerService }] });
const jsonata$2 = jsonata_;
class FlexyFlexyFormIfDirective {
templateRef;
viewContainer;
flexyFormIf;
constructor(templateRef, viewContainer) {
this.templateRef = templateRef;
this.viewContainer = viewContainer;
}
_visibility = true;
_changesSubscription;
_ifCompiled;
_viewRef;
ngOnInit() {
if (this.flexyFormIf && this.flexyFormIf.schema.if && this.flexyFormIf.form) {
this._visibility = this._isEnabled(this.flexyFormIf.form.currentData);
this._changesSubscription = this.flexyFormIf.form.currentData$.subscribe(data => {
this._visibility = this._isEnabled(data);
this._render();
});
}
this._render();
}
_render() {
if (this._visibility) {
if (!this._viewRef) {
this.viewContainer.clear();
if (this.templateRef) {
this._viewRef = this.viewContainer.createEmbeddedView(this.templateRef);
}
}
}
else {
this.viewContainer.clear();
this._viewRef = null;
}
}
_isEnabled(data) {
const schema = this.flexyFormIf.schema;
let is = false;
if (schema.if) {
if (!this._ifCompiled) {
this._ifCompiled = jsonata$2(schema.if);
}
try {
is = !!this._ifCompiled.evaluate(data);
}
catch (e) {
// do nothing
}
}
return is;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFlexyFormIfDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.15", type: FlexyFlexyFormIfDirective, isStandalone: false, selector: "[flexyFormIf]", inputs: { flexyFormIf: "flexyFormIf" }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFlexyFormIfDirective, decorators: [{
type: Directive,
args: [{
selector: '[flexyFormIf]',
standalone: false
}]
}], ctorParameters: () => [{ type: i0.TemplateRef }, { type: i0.ViewContainerRef }], propDecorators: { flexyFormIf: [{
type: Input
}] } });
class FlexyFormContainerComponent {
form;
schema;
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: FlexyFormContainerComponent, isStandalone: false, selector: "flexy-form-container", inputs: { form: "form", schema: "schema" }, ngImport: i0, template: `
<ng-template #tmplRef let-schema let-form="form">
<ng-container *ngFor="let schemaItem of schema">
<ng-container *flexyFormIf="{ schema: schemaItem, form: form }">
<ng-container
*ngIf="schemaItem.componentType"
flexyFormContainer
[componentSchema]="schemaItem"
[flexyForm]="form"
></ng-container>
<ng-container *ngIf="!schemaItem.componentType">
<div flexyFormAttributes [componentSchema]="schemaItem" [flexyForm]="form">
<ng-container *ngIf="schemaItem.children">
<ng-container *ngTemplateOutlet="tmplRef; context: { $implicit: schemaItem.children, form: form }"></ng-container>
</ng-container>
</div>
</ng-container>
</ng-container>
</ng-container>
</ng-template>
<ng-content></ng-content>
<ng-container *ngTemplateOutlet="tmplRef; context: { $implicit: schema, form: form }"></ng-container>
`, isInline: true, dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: FlexyFormAttributesDirective, selector: "[flexyFormAttributes]", inputs: ["flexyForm", "componentSchema"] }, { kind: "directive", type: FlexyFormContainerDirective, selector: "[flexyFormContainer]", inputs: ["flexyForm", "componentSchema"] }, { kind: "directive", type: FlexyFlexyFormIfDirective, selector: "[flexyFormIf]", inputs: ["flexyFormIf"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormContainerComponent, decorators: [{
type: Component,
args: [{
selector: 'flexy-form-container',
template: `
<ng-template #tmplRef let-schema let-form="form">
<ng-container *ngFor="let schemaItem of schema">
<ng-container *flexyFormIf="{ schema: schemaItem, form: form }">
<ng-container
*ngIf="schemaItem.componentType"
flexyFormContainer
[componentSchema]="schemaItem"
[flexyForm]="form"
></ng-container>
<ng-container *ngIf="!schemaItem.componentType">
<div flexyFormAttributes [componentSchema]="schemaItem" [flexyForm]="form">
<ng-container *ngIf="schemaItem.children">
<ng-container *ngTemplateOutlet="tmplRef; context: { $implicit: schemaItem.children, form: form }"></ng-container>
</ng-container>
</div>
</ng-container>
</ng-container>
</ng-container>
</ng-template>
<ng-content></ng-content>
<ng-container *ngTemplateOutlet="tmplRef; context: { $implicit: schema, form: form }"></ng-container>
`
// ,
// changeDetection: ChangeDetectionStrategy.OnPush
,
standalone: false
}]
}], propDecorators: { form: [{
type: Input
}], schema: [{
type: Input
}] } });
const CHNAGES_DEBOUNCE_TIME = 50;
class FlexyFormComponent {
jsonMapper;
cdr;
json;
data;
readonly = false;
changesDebounceTime = CHNAGES_DEBOUNCE_TIME;
created = new EventEmitter();
changed = new EventEmitter();
form;
_changesSubscription;
constructor(jsonMapper, cdr) {
this.jsonMapper = jsonMapper;
this.cdr = cdr;
}
ngOnInit() {
this.form = this.jsonMapper.createForm(cloneDeep(this.json), this.readonly, this.data);
this.created.emit(this.form);
this._changesSubscription = this.form.currentData$
.pipe(skip(1), debounceTime(this.changesDebounceTime || CHNAGES_DEBOUNCE_TIME))
.subscribe(data => {
this.changed.emit({ valid: this.form.valid, data });
});
}
ngOnDestroy() {
if (this._changesSubscription) {
this._changesSubscription.unsubscribe();
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormComponent, deps: [{ token: FlexyFormJsonMapperService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: FlexyFormComponent, isStandalone: false, selector: "flexy-form", inputs: { json: "json", data: "data", readonly: "readonly", changesDebounceTime: "changesDebounceTime" }, outputs: { created: "created", changed: "changed" }, ngImport: i0, template: `
<flexy-form-container *ngIf="form" [form]="form" [schema]="form.schema"></flexy-form-container>
`, isInline: true, dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FlexyFormContainerComponent, selector: "flexy-form-container", inputs: ["form", "schema"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormComponent, decorators: [{
type: Component,
args: [{
selector: 'flexy-form',
template: `
<flexy-form-container *ngIf="form" [form]="form" [schema]="form.schema"></flexy-form-container>
`,
standalone: false
}]
}], ctorParameters: () => [{ type: FlexyFormJsonMapperService }, { type: i0.ChangeDetectorRef }], propDecorators: { json: [{
type: Input
}], data: [{
type: Input
}], readonly: [{
type: Input
}], changesDebounceTime: [{
type: Input
}], created: [{
type: Output
}], changed: [{
type: Output
}] } });
const jsonata$1 = jsonata_;
class FlexyFormAbstractOptionsComponent {
optionsService;
logger;
form;
// inputs
options;
optionsUrl;
optionsPath;
optionsMapper;
optionsRawId;
optionsFilter;
loading;
changesSubscription;
filterData = {};
_optionsCache;
constructor(optionsService, logger) {
this.optionsService = optionsService;
this.logger = logger;
}
async initOptions() {
if (this.optionsUrl) {
this.loading = true;
const options = await this.optionsService.loadOptions(this.optionsUrl, this.optionsMapper, this.optionsPath).toPromise();
this._optionsCache = options;
this.loading = false;
if (this.optionsFilter && this.optionsFilter.filter) {
this._setOptionsFiltering();
}
else {
this.options = options;
}
}
else {
this.loading = false;
}
}
_setOptionsFiltering() {
try {
const jsonataExp = jsonata$1(this.optionsFilter.filter);
this.logger.debug('jsonata', this.optionsFilter.filter);
this.changesSubscription = this.form.currentData$.subscribe(data => {
let isChanged = false;
this.optionsFilter.observableFields.forEach(path => {
const vp = get(data, path);
if (get(this.filterData, path) !== vp) {
set(this.filterData, path, vp);
isChanged = true;
}
});
if (isChanged) {
this.logger.debug('evaluate', {
optionsList: this._optionsCache,
observableFields: this.filterData
});
const jsonOptions = jsonataExp.evaluate({
optionsList: this._optionsCache,
observableFields: this.filterData
});
const options = jsonOptions ? (Array.isArray(jsonOptions) ? jsonOptions : [jsonOptions]) : [];
this.options = options.filter(item => !!item);
}
});
}
catch (e) {
this.logger.error('Wrong jsonata expresion', this.optionsFilter.filter, e);
}
}
}
const REQUIRED_PATTERN_KEY = 'requiredPattern';
class FlexyFormFirstErrorPipe {
translateService;
logger;
constructor(translateService, logger) {
this.translateService = translateService;
this.logger = logger;
}
transform(errors) {
if (errors && Object.keys(errors) && Object.keys(errors).length) {
const key = Object.keys(errors)[0];
const error = typeof errors[key] === 'object' ? { ...errors[key] } : '';
this.logger.debug('translate', key, errors[key]);
if (key === 'pattern') {
const patternKey = 'FLEXY_FORM_VALIDATION_PATTERN_' + error[REQUIRED_PATTERN_KEY];
const pattern = this.translateService.instant(patternKey);
if (pattern && pattern !== patternKey) {
error[REQUIRED_PATTERN_KEY] = pattern;
}
this.logger.debug('Validation error message: Check pattern translate', patternKey);
}
return this.translateService.instant('FLEXY_FORM_VALIDATION_' +
key
.split('-')
.join('_')
.toUpperCase(), error);
}
return '';
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormFirstErrorPipe, deps: [{ token: i1$2.TranslateService }, { token: i2$1.FlexyLoggerService }], target: i0.ɵɵFactoryTarget.Pipe });
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormFirstErrorPipe, isStandalone: false, name: "firstError" });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormFirstErrorPipe, decorators: [{
type: Pipe,
args: [{
name: 'firstError',
standalone: false
}]
}], ctorParameters: () => [{ type: i1$2.TranslateService }, { type: i2$1.FlexyLoggerService }] });
class FlexyOptionsMapperPipe {
constructor() { }
transform(value, mapper) {
if (!value || !Array.isArray(value) || !mapper || !mapper.value || !mapper.text) {
return [];
}
else {
return value
.filter(item => !!item && item[mapper.value])
.map(item => ({
value: item[mapper.value],
text: item[mapper.text]
}));
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyOptionsMapperPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: FlexyOptionsMapperPipe, isStandalone: false, name: "flexyOptions" });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyOptionsMapperPipe, decorators: [{
type: Pipe,
args: [{
name: 'flexyOptions',
standalone: false
}]
}], ctorParameters: () => [] });
class FlexyFormSchemaService {
jsonMapperService;
constructor(jsonMapperService) {
this.jsonMapperService = jsonMapperService;
}
addGroupItemToSchema(parentSchemaRef, itemKey, groupSchema, controlGroupName, readonly) {
let schema = null;
if (!this.validateGroupKey(itemKey, groupSchema)) {
const jsonSchema = cloneDeep(groupSchema.items);
jsonSchema.name = controlGroupName;
jsonSchema.type = FlexyFormFieldType.Group;
const newValue = jsonSchema && jsonSchema.children ? {} : null;
const control = this.jsonMapperService.createItemControl(jsonSchema, readonly, newValue);
parentSchemaRef.formControl.addControl(itemKey, control);
parentSchemaRef.formControl.markAsDirty();
schema = this.jsonMapperService.createGroupItemSchema(control, jsonSchema, groupSchema.indexDef, null, readonly, {}, newValue, itemKey, parentSchemaRef.formControl);
parentSchemaRef.children.push(schema);
// fix form control - should be the same but is not
parentSchemaRef.formControl.setControl(itemKey, schema.formControl);
}
return schema;
}
validateGroupKey(key, groupSchema) {
const patternReg = groupSchema.indexPattern ? new RegExp(groupSchema.indexPattern, 'g') : null;
if (!key) {
return 'empty';
}
else if (patternReg && !patternReg.exec(key)) {
return 'wrong_format';
}
return null;
}
removeGroupItemToSchema(layoutSchemaRef, index) {
if (layoutSchemaRef.children[index]) {
const schemaItem = layoutSchemaRef.children[index];
const groupKey = schemaItem.groupKey;
if (groupKey &&
layoutSchemaRef.formControl &&
layoutSchemaRef.formControl instanceof FormGroup &&
layoutSchemaRef.formControl.contains(groupKey)) {
layoutSchemaRef.children.splice(index, 1);
layoutSchemaRef.formControl.removeControl(groupKey);
layoutSchemaRef.formControl.markAsDirty();
return groupKey;
}
}
return null;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormSchemaService, deps: [{ token: FlexyFormJsonMapperService }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormSchemaService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormSchemaService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: FlexyFormJsonMapperService }] });
function getSchemaData(schema) {
const fg = new FormGroup({});
const ff = new FlexyForm(fg, [schema], {});
const allData = ff.getAllData();
if (schema.formName) {
return get(allData, schema.formName);
}
else {
return allData;
}
}
const jsonata = jsonata_;
class FlexyFormControlOptionsService {
httpClient;
logger;
constructor(httpClient, logger) {
this.httpClient = httpClient;
this.logger = logger;
}
loadOptions(optionsUrl, optionsMapper = null, optionsPath) {
if (optionsUrl) {
return this.httpClient.get(optionsUrl).pipe(map(data => {
if (data && optionsPath) {
data = get(data, optionsPath);
}
const options = [];
if (data && Array.isArray(data) && optionsMapper) {
if (typeof optionsMapper === 'string') {
options.push(...this._jsonataMapperOptions(optionsMapper, data));
}
else {
options.push(...this._optionsMapper(optionsMapper, data));
}
}
else if (!optionsMapper) {
this.logger.warn('Options loader: optionsMapper is required');
}
return options;
}));
}
}
_jsonataMapperOptions(optionsMapper, data) {
const options = [];
try {
const compiled = jsonata(optionsMapper);
const jsonOptions = compiled.evaluate({ optionsData: data });
if (jsonOptions &&
Array.isArray(jsonOptions) &&
jsonOptions.length &&
jsonOptions[0].hasOwnProperty('text') &&
jsonOptions[0].hasOwnProperty('value')) {
options.push(...jsonOptions);
}
else {
this.logger.debug('Jsonata error debug', jsonOptions, optionsMapper, data);
this.logger.warn('Wrong jsonata mapping');
}
}
catch (e) {
this.logger.error('Wrong jsonata expresion', optionsMapper, e);
}
return options;
}
_optionsMapper(optionsMapper, data) {
const textTemplate = this._mapperTemplate(optionsMapper.text);
const valueTemplate = this._mapperTemplate(optionsMapper.value);
const prefixTemplate = this._mapperTemplate(optionsMapper.prefixHtml);
const options = [];
if (data && data.length) {
data.forEach(item => {
options.push({
text: item && textTemplate ? textTemplate(item) : '' + get(item, optionsMapper.text),
value: item && valueTemplate ? valueTemplate(item) : get(item, optionsMapper.value),
prefixHtml: item && prefixTemplate ? prefixTemplate(item) : get(item, optionsMapper.prefixHtml),
_raw: item
});
});
}
return options;
}
_mapperTemplate(mapper) {
if (!mapper) {
return;
}
// check if template?
if (mapper.includes('<%')) {
return template(mapper);
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormControlOptionsService, deps: [{ token: i1$3.HttpClient }, { token: i2$1.FlexyLoggerService }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormControlOptionsService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormControlOptionsService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: i1$3.HttpClient }, { type: i2$1.FlexyLoggerService }] });
function findRawValue(rawIdKey, controlValue, options) {
if (rawIdKey) {
if (controlValue && Array.isArray(controlValue)) {
const ret = [];
controlValue.forEach(valueItem => {
const v = valueItem && valueItem[rawIdKey];
if (v) {
const el = options && options.find(item => item && item._raw && item._raw[rawIdKey] === v);
if (el && el.value) {
ret.push(el.value);
}
}
});
return ret;
}
else {
if (controlValue && typeof controlValue === 'object' && controlValue[rawIdKey]) {
const el = options.find(item => item && item._raw && item._raw[rawIdKey] === controlValue[rawIdKey]);
return el ? el.value : null;
}
}
}
else {
return controlValue;
}
return;
}
function prepareControlValue(optionsRawId, data) {
if (data) {
const cloned = cloneDeep(data);
if (optionsRawId) {
if (Array.isArray(cloned)) {
cloned.forEach(item => {
item.value = item && item._raw;
});
}
else {
cloned.value = cloned && cloned._raw;
}
}
return Array.isArray(cloned) ? cloned.map(el => el && el.value) : cloned.value;
}
else {
return null;
}
}
function isRequired(control) {
if (!control) {
return null;
}
let required = false;
const formControl = new FormControl();
if (control.validator) {
const validationResult = control.validator(formControl);
required = validationResult !== null && validationResult.required === true;
}
return required;
}
function markAsDirtyDeep(control) {
control.markAsDirty();
if (control instanceof FormGroup) {
Object.keys(control.controls).forEach(key => markAsDirtyDeep(control.controls[key]));
}
else if (control instanceof FormArray) {
control.controls.forEach(child => markAsDirtyDeep(child));
}
}
const PUBLIC_COMPONENTS = [
FlexyFormFirstErrorPipe,
FlexyOptionsMapperPipe,
FlexyFormContainerComponent,
FlexyFormAttributesDirective,
FlexyFormContainerDirective,
FlexyFlexyFormIfDirective,
FlexyFormComponent
];
class FlexyFormsModule {
static forRoot(validators) {
return {
ngModule: FlexyFormsModule,
providers: [
{
provide: FLEXY_FORM_VALIDATORS,
useValue: validators,
},
],
};
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormsModule, declarations: [FlexyFormFirstErrorPipe,
FlexyOptionsMapperPipe,
FlexyFormContainerComponent,
FlexyFormAttributesDirective,
FlexyFormContainerDirective,
FlexyFlexyFormIfDirective,
FlexyFormComponent], imports: [CommonModule, FormsModule, ReactiveFormsModule, TranslateModule, FlexyLayoutModule], exports: [FlexyFormFirstErrorPipe,
FlexyOptionsMapperPipe,
FlexyFormContainerComponent,
FlexyFormAttributesDirective,
FlexyFormContainerDirective,
FlexyFlexyFormIfDirective,
FlexyFormComponent] });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormsModule, imports: [CommonModule, FormsModule, ReactiveFormsModule, TranslateModule, FlexyLayoutModule] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FlexyFormsModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule, FormsModule, ReactiveFormsModule, TranslateModule, FlexyLayoutModule],
declarations: [...PUBLIC_COMPONENTS],
exports: [...PUBLIC_COMPONENTS],
}]
}] });
/*
* Public API Surface of form
*/
/**
* Generated bundle index. Do not edit.
*/
export { COMPLEX_TYPE_INDEX_MARKER, FLEXY_FORM_VALIDATORS, FlexyFlexyFormIfDirective, FlexyForm, FlexyFormAbstractOptionsComponent, FlexyFormAttributesDirective, FlexyFormComponent, FlexyFormContainerComponent, FlexyFormContainerDirective, FlexyFormControlOptionsService, FlexyFormDataMode, FlexyFormFieldType, FlexyFormFirstErrorPipe, FlexyFormJsonMapperService, FlexyFormSchemaService, FlexyFormsModule, FlexyFormsValidators, FlexyOptionsMapperPipe, HIDDEN_CALC_GROUP_NAME, HIDDEN_IF_GROUP_NAME, assignHiddenNames, calculate, checkSchema, clearEmptyArrayAndObjects, findErrors, findRawValue, findRemoved, findSchema, getSchemaData$1 as getSchemaData, isRequired, markAsDirtyDeep, parseFormJson, parseFormVersion1, parseFormVersion1Item, prepareControlValue, replaceMarker };
//# sourceMappingURL=ng-flexy-form.mjs.map