@formql/core
Version:
FormQL - A framework for building dynamic forms
1,177 lines (1,164 loc) • 103 kB
JavaScript
import { __decorate, __metadata, __param } from 'tslib';
import { EventEmitter, Input, Output, Component, Injectable, ComponentFactoryResolver, ɵɵdefineInjectable, ɵɵinject, Inject, Directive, ViewChild, ViewContainerRef, ChangeDetectionStrategy, ElementRef, Renderer2, TemplateRef, HostListener, forwardRef, NgModule } from '@angular/core';
import { FormGroup, FormControl, FormBuilder, NG_VALUE_ACCESSOR, NG_VALIDATORS, Validators, ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { UUID } from 'angular2-uuid';
import { Subject } from 'rxjs';
import { map, concatMap, takeUntil } from 'rxjs/operators';
import { TextMaskModule } from 'angular2-text-mask';
import { createAutoCorrectedDatePipe, createNumberMask } from 'text-mask-addons';
var ContainerType;
(function (ContainerType) {
ContainerType[ContainerType["Section"] = 1] = "Section";
ContainerType[ContainerType["Component"] = 2] = "Component";
ContainerType[ContainerType["Page"] = 3] = "Page";
})(ContainerType || (ContainerType = {}));
var FormQLMode;
(function (FormQLMode) {
FormQLMode[FormQLMode["View"] = 0] = "View";
FormQLMode[FormQLMode["Edit"] = 1] = "Edit";
})(FormQLMode || (FormQLMode = {}));
let PlainLayoutComponent = class PlainLayoutComponent {
constructor() {
this.submit = new EventEmitter();
}
onSubmitTriggered() {
this.submit.emit(null);
}
};
PlainLayoutComponent.componentName = 'PlainLayoutComponent';
__decorate([
Input(),
__metadata("design:type", Object)
], PlainLayoutComponent.prototype, "form", void 0);
__decorate([
Input(),
__metadata("design:type", FormGroup)
], PlainLayoutComponent.prototype, "reactiveForm", void 0);
__decorate([
Input(),
__metadata("design:type", Number)
], PlainLayoutComponent.prototype, "mode", void 0);
__decorate([
Output(),
__metadata("design:type", Object)
], PlainLayoutComponent.prototype, "submit", void 0);
PlainLayoutComponent = __decorate([
Component({
selector: 'formql-plain-layout',
template: `<ng-container *ngIf="form && form.pages && form.pages.length > 0">
<form [formGroup]="reactiveForm" (ngSubmit)="onSubmitTriggered()">
<div formql-page-wrapper *ngIf="form.pages"
[(page)]="form.pages[0]"
[reactivePage]="reactiveForm.controls[form.pages[0].pageId]"
[formGroupName]="form.pages[0].pageId"
[mode]="mode"></div>
</form>
</ng-container>`
})
], PlainLayoutComponent);
let DndService = class DndService {
synchroniseSectionModel(page, event) {
let sourceSection = page.sections.find(s => s.sectionId === event.sourceWrapperId);
let sourceComponent = null;
if (event.sourceObjectId === 'new') {
sourceComponent = this.newComponent();
event.sourceObjectId = sourceComponent.componentId;
sourceSection = page.sections.find(s => s.sectionId === event.targetWrapperId);
if (sourceSection)
sourceSection.components.push(sourceComponent);
else
return page; // this should never happen
}
else
sourceComponent = sourceSection.components.find(c => c.componentId === event.sourceObjectId);
if (!sourceComponent)
return;
// has the component been moved across position type (header/body)?
if (sourceComponent.position.type !== event.positionType)
sourceComponent.position.type = event.positionType;
// has the component been placed in a differnet CSS grid area?
if (sourceComponent.position.id !== event.targetPositionId)
sourceComponent.position.id = event.targetPositionId;
if (event.sourceWrapperId !== event.targetWrapperId) {
let targetSection = this.transferComponent(page, sourceSection, event);
targetSection.template.reRender = true;
targetSection = this.reorderComponents(targetSection, sourceComponent, event);
}
else
sourceSection = this.reorderComponents(sourceSection, sourceComponent, event);
sourceSection.template.reRender = true;
return page;
}
synchronisePageModel(page, event) {
let sourceSection = null;
if (event.sourceObjectId === 'new') {
sourceSection = this.newSection();
page.sections.push(sourceSection);
}
else
sourceSection = page.sections.find(s => s.sectionId === event.sourceObjectId);
// can't find the source section, model must be in different page
if (sourceSection != null) {
if (sourceSection.position.id !== event.targetPositionId)
sourceSection.position.id = event.targetPositionId;
page.template.reRender = true;
page = this.reorderSections(page, sourceSection, event);
}
return page;
}
reorderComponents(section, sourceCompoment, event) {
let components = section.components.filter(c => c.position.id === sourceCompoment.position.id);
const targetComponent = components.find(c => c.componentId === event.targetIndexId);
if (targetComponent) {
sourceCompoment.position.index = targetComponent.position.index;
targetComponent.position.index = targetComponent.position.index + 0.5;
}
components = components.sort((left, right) => {
return left.position.index - right.position.index;
});
for (let i = 0; i < components.length; i++) {
components[i].position.index = i;
}
return section;
}
reorderSections(page, sourceSection, event) {
let sections = page.sections.filter(c => c.position.id === sourceSection.position.id);
const targetSection = sections.find(c => c.sectionId === event.targetIndexId);
if (targetSection) {
sourceSection.position.index = targetSection.position.index;
targetSection.position.index = targetSection.position.index + 0.5;
}
sections = sections.sort((left, right) => {
return left.position.index - right.position.index;
});
for (let i = 0; i < sections.length; i++)
sections[i].position.index = i;
return page;
}
transferComponent(page, sourceSection, event) {
const targetSection = page.sections.find(s => s.sectionId === event.targetWrapperId);
if (!targetSection)
return null;
const component = sourceSection.components.find(c => c.componentId === event.sourceObjectId);
if (!component)
return null;
const index = sourceSection.components.findIndex(s => s.componentId === event.sourceObjectId);
targetSection.components.push(component);
sourceSection.components.splice(index, 1);
return targetSection;
}
newSection() {
return {
sectionId: UUID.UUID(),
sectionName: 'New section',
components: [],
template: {
header: {
gridTemplateColumns: '1fr',
gridTemplateRows: '1fr',
gridTemplateAreas: '"ID1_1"'
},
body: {
gridTemplateColumns: '1fr',
gridTemplateRows: '1fr',
gridTemplateAreas: '"ID1_1"'
}
},
position: {
id: '-1',
index: 0
}
};
}
newComponent() {
return {
componentId: UUID.UUID(),
label: 'New Component',
componentName: 'FormQLLabelComponent',
position: {
id: '-1',
index: 0
}
};
}
};
DndService = __decorate([
Injectable()
], DndService);
var GridPositionType;
(function (GridPositionType) {
GridPositionType[GridPositionType["Header"] = 1] = "Header";
GridPositionType[GridPositionType["Body"] = 2] = "Body";
GridPositionType[GridPositionType["Footer"] = 3] = "Footer";
})(GridPositionType || (GridPositionType = {}));
var InternalEventType;
(function (InternalEventType) {
// Editing events
InternalEventType[InternalEventType["EditingComponent"] = 1] = "EditingComponent";
InternalEventType[InternalEventType["EditingSection"] = 2] = "EditingSection";
InternalEventType[InternalEventType["EditingPage"] = 3] = "EditingPage";
InternalEventType[InternalEventType["EditingForm"] = 4] = "EditingForm";
InternalEventType[InternalEventType["RemoveComponent"] = 5] = "RemoveComponent";
InternalEventType[InternalEventType["RemoveSection"] = 6] = "RemoveSection";
InternalEventType[InternalEventType["RemovePage"] = 7] = "RemovePage";
// Form changes
InternalEventType[InternalEventType["DndFormChanged"] = 8] = "DndFormChanged";
})(InternalEventType || (InternalEventType = {}));
let ComponentResolverService = class ComponentResolverService {
constructor(componentFactoryResolver) {
this.componentFactoryResolver = componentFactoryResolver;
this.componentRegister = {};
}
resolveComponent(componentName) {
if (!this.componentRegister) {
return null;
}
const component = this.componentRegister[componentName];
if (!component) {
console.log(`Component ${componentName} not found.`);
return null;
}
const resolvedComponent = this.componentFactoryResolver.resolveComponentFactory(component);
return resolvedComponent;
}
addComponents(...components) {
if (Array.isArray(components)) {
components.forEach(c => this.componentRegister[c.componentName] = c);
}
}
addComponent(component) {
this.componentRegister[component.componentName] = component;
}
getComponentArray() {
return Array.from(Object.keys(this.componentRegister), x => this.componentRegister[x]);
}
};
ComponentResolverService = __decorate([
Injectable(),
__metadata("design:paramtypes", [ComponentFactoryResolver])
], ComponentResolverService);
var HelperService_1;
let HelperService = HelperService_1 = class HelperService {
static evaluateCondition(condition, data) {
let response = { value: false, error: null };
if (condition && condition.trim() !== '' && condition !== 'false') {
if (condition === 'true') {
response.value = true;
return response;
}
if (!data)
return response;
response = Object.assign({}, this.evaluate(condition, data));
if (response.value !== true)
response.value = false;
}
return response;
}
static evaluateValue(path, data) {
let response = { value: null, error: null };
if (!data)
return response;
response = Object.assign({}, this.evaluate(path, data));
if (Number.isNaN(response.value) || response.value === Infinity)
response.value = null;
else
response.value = this.deepCopy(response.value);
return response;
}
static evaluate(path, data) {
const response = { value: null, error: null };
const props = Object.keys(data);
const params = [];
for (let i = 0; i < props.length; i++)
params.push(data[props[i]]);
params.push(path);
props.push('path');
const expression = `
'use strict'
let window = undefined;
let document = undefined;
let alert = undefined;
let a = undefined;
return ${path};
`;
props.push(expression);
try {
const evalFunc = new Function(...props);
response.value = evalFunc(...params);
}
catch (err) {
response.error = err;
}
return response;
}
static setValue(schema, value, data) {
if (value === undefined)
value = null;
if (schema) {
if (!data)
data = {};
let key = schema;
if (schema.indexOf('.') !== -1) {
const arr = schema.split('.');
let item = data;
for (let i = 0; i <= arr.length - 1; i++) {
key = arr[i];
if (!item[key])
item[key] = {};
if (i !== arr.length - 1)
item = item[key];
}
item[key] = value;
}
else
data[key] = value;
}
return data;
}
static getValue(schema, data, type) {
if (!schema || !data || (data && Object.keys(data).length === 0 && data.constructor === Object))
return;
try {
const evalFunc = new Function('data', `return data.${schema};`);
return HelperService_1.resolveType(evalFunc(data), type);
}
catch (err) {
return null;
}
}
static setValidators(componentResolverService, component, control) {
const componentRef = componentResolverService.resolveComponent(component.componentName);
if (!componentRef)
return control;
const type = componentRef.componentType;
if (type && (!type['validators'] || (type['validators'] && type['validators'].length === 0)))
return control;
const validators = [];
const rules = component.rules;
if (rules != null) {
const FormValidators = type['validators'];
Object.keys(rules).forEach((key) => {
const item = rules[key];
if (item.value && item.key !== 'readonly' && item.key !== 'hidden' && item.key !== 'value') {
const validator = FormValidators.find((x) => x.key === item.key);
if (validator && validator.validator)
validators.push(validator.validator);
}
else if (item.value && item.key === 'readonly' && control.enabled)
control.disable();
});
if (control.disabled &&
(!rules || (rules && !rules.readonly) || (rules && rules.readonly && !rules.readonly.value)))
control.enable();
}
if (validators.length > 0)
control.setValidators(validators);
return control;
}
static createReactiveFormStructure(form, initialiseData = true, data = null) {
const formControls = {};
const components = {};
const pageGroup = new FormGroup({});
form.pages.forEach((page) => {
const sectionGroup = {};
if (page.sections != null)
page.sections.forEach((section) => {
const componentGroup = {};
if (section.components != null)
section.components.forEach((component) => {
components[component.componentId] = component;
const singleComponentGroup = new FormControl();
formControls[component.componentId] = singleComponentGroup;
componentGroup[component.componentId] = singleComponentGroup;
if (initialiseData) {
if (!data)
data = {};
data = HelperService_1.instantiateData(data, component.schema);
try {
const value = this.getValue(component.schema, data, component.type);
if (value) {
formControls[component.componentId].setValue(value);
component.value = value;
}
}
catch (err) {
throw err;
}
}
});
sectionGroup[section.sectionId] = new FormGroup(componentGroup);
});
pageGroup[page.pageId] = new FormGroup(sectionGroup);
});
return { pageGroup: pageGroup, formControls: formControls, components: components, data: data };
}
static instantiateData(data, schema) {
if (schema && schema.indexOf('.') !== -1) {
const arr = schema.split('.');
let item = data;
let key = '';
for (let i = 0; i <= arr.length - 2; i++) {
key = arr[i];
if (!item[key])
item[key] = {};
if (i !== arr.length - 2)
item = item[key];
}
}
return data;
}
static deepCopy(oldObj, ignoreProperty = null) {
let newObj = oldObj;
if (oldObj && typeof oldObj === 'object') {
newObj = Object.prototype.toString.call(oldObj) === '[object Array]' ? [] : {};
for (const i in oldObj)
if (!ignoreProperty || (ignoreProperty && !ignoreProperty.find((p) => p === i)))
newObj[i] = this.deepCopy(oldObj[i]);
}
return newObj;
}
static propertyCopy(source, target, ignoreProperties = null) {
if (source && typeof source === 'object')
for (const i in source)
if (!ignoreProperties || (ignoreProperties && !ignoreProperties.find((p) => p === i)))
if (source[i] && typeof source[i] === 'object') {
if (!target[i])
target[i] = {};
target[i] = this.propertyCopy(source[i], target[i]);
}
else
target[i] = source[i];
else
console.log(`propertyCopy function doesn't support primitives`);
return target;
}
static formatForGraphQl(obj) {
const updatedData = this.deepCopy(obj);
if (updatedData['__typename'])
delete updatedData['__typename'];
let dataForQuery = '';
Object.keys(updatedData).forEach((fieldName) => {
if (updatedData[fieldName] == null)
dataForQuery += fieldName + ': null,';
else if (typeof updatedData[fieldName] === 'object')
dataForQuery += this.formatForGraphQl(updatedData[fieldName]);
else if (typeof updatedData[fieldName] === 'number' || typeof updatedData[fieldName] === 'boolean')
dataForQuery += fieldName + `:${updatedData[fieldName]},`;
else
dataForQuery += fieldName + `:\"${updatedData[fieldName]},`;
});
dataForQuery = `{${dataForQuery.slice(0, -1)}}`;
return dataForQuery;
}
static formatError(error) {
if (!error)
return;
if (error.error && error.error.message)
error.message = error.error.message;
return error;
}
static resolveType(value, type) {
if (value === null || value === undefined || value === '')
return null;
else if (Number.isNaN(value))
return 0;
switch (type) {
case 'number':
if (typeof value === 'string')
value = value.replace(/[^\d\.]/g, '');
return Number(value);
default:
return value;
}
}
static maskToArray(mask) {
const result = [];
if (mask) {
const maskTrimmed = mask.trim().substring(1).slice(0, -1).replace('\\\\', '\\');
const arry = maskTrimmed.split(',');
arry.forEach((item) => {
result.push(item.trim().replace(/\"/g, '').replace(/\'/g, ''));
});
}
return result;
}
static updateTemplates(form) {
form.pages.forEach((page) => {
page.template.reRender = false;
page.template = HelperService_1.deepCopy(page.template);
page.sections.forEach((section) => {
section.template.reRender = false;
section.template = HelperService_1.deepCopy(section.template);
});
});
return form;
}
static resetValidators(components, formControls, componentResolverService) {
if (components && Object.keys(components).length > 0)
Object.keys(components).forEach((key) => {
const component = components[key];
if (component) {
let componentControl = formControls[component.componentId];
if (componentControl)
componentControl = HelperService_1.setValidators(componentResolverService, component, componentControl);
}
});
return formControls;
}
static validateForm(formGroup) {
Object.keys(formGroup.controls).forEach((field) => {
const control = formGroup.get(field);
if (control instanceof FormControl)
control.markAsTouched({ onlySelf: true });
else if (control instanceof FormGroup)
this.validateForm(control);
});
}
};
HelperService.ɵprov = ɵɵdefineInjectable({ factory: function HelperService_Factory() { return new HelperService(); }, token: HelperService, providedIn: "root" });
HelperService = HelperService_1 = __decorate([
Injectable({
providedIn: 'root'
})
], HelperService);
class RuleLogic {
constructor() {
this.evalFunctions = ['GET', 'SUM'];
}
/*
Perform a condition evaluation
*/
doEval(condition, conditionFunctions) {
const conditionFunctionsDeclares = Object.keys(conditionFunctions).map(x => `let ${x} = conditionFunctions.${x}; `).join('');
if (condition.trim() === '')
return;
if (this.evalFunctions.indexOf(condition.trim()) !== -1)
throw Error(`Funcitons need a parameter (e.g. GET('contact.firstName') )`);
const props = [];
const params = [];
params.push(condition);
params.push(conditionFunctions);
props.push('condition');
props.push('conditionFunctions');
const expression = `
'use strict'
${conditionFunctionsDeclares}
let window = undefined;
let document = undefined;
let alert = undefined;
let a = undefined;
return ${condition};
`;
props.push(expression);
try {
const evalFunc = new Function(...props);
const response = evalFunc(...params);
return response;
}
catch (err) {
debugger; // intentionally left to help troubleshooting issues
console.log(err);
throw err;
}
}
/*
Reset all dependancies for any given condition in a component
*/
resetDependancies(formState, condition, component) {
const self = this;
const registerFunctions = {
GET(schema) {
formState.components = self.setDependents(formState.components, schema, component.componentId);
},
SUM(...schemas) {
schemas.forEach(schema => formState.components = self.setDependents(formState.components, schema, component.componentId));
}
};
return this.doEval(condition, registerFunctions);
}
/*
Evaluetes the value of any given condition
*/
evaluate(data, condition) {
const self = this;
const evalFunctions = {
GET(schema) {
const result = self.getSchemaValue(data, schema);
if (result !== undefined && result !== null)
return result;
else
return '';
},
SUM(...schemas) {
let total = 0;
schemas.forEach(schema => {
const value = self.getSchemaValue(data, schema);
if (value && !isNaN(value))
total += value;
});
return total;
}
};
return this.doEval(condition, evalFunctions);
}
evaluateCondition(data, condition) {
const response = { value: false, error: null };
if (condition && condition.trim() !== '' && condition !== 'false') {
if (condition === 'true') {
response.value = true;
return response;
}
if (!data)
return response;
try {
response.value = this.evaluate(data, condition);
}
catch (err) {
response.error = err;
}
if (response.value !== true)
response.value = false;
}
return response;
}
evaluateValue(data, expression) {
const response = { value: null, error: null };
if (!data)
return response;
try {
response.value = this.evaluate(data, expression);
}
catch (err) {
response.error = err;
return response;
}
if (Number.isNaN(response.value) || response.value === Infinity)
response.value = null;
else
response.value = HelperService.deepCopy(response.value);
return response;
}
getSchemaValue(data, schema) {
const evalFunc = new Function('data', 'schema', `return data.${schema}`);
return evalFunc(data, schema);
}
setDependents(components, schema, componentId) {
Object.keys(components).forEach((key) => {
const component = components[key];
if (component.schema === schema) {
if (!component.dependents)
component.dependents = [componentId];
else if (component.dependents.indexOf(componentId) === -1)
component.dependents.push(componentId);
}
});
return components;
}
}
let FormService = class FormService {
constructor(srv, componentResolverService, formBuilder) {
this.componentResolverService = componentResolverService;
this.formBuilder = formBuilder;
this.ruleLogic = new RuleLogic();
this.injectedService = srv;
}
getFormAndData(formName, ids) {
if (ids)
return this.injectedService.getForm(formName).pipe(map((response) => response), concatMap((model) => this.injectedService.getData(model.dataSource, ids).pipe(map((data) => this.initialiseFormState(model, data)))));
else
return this.injectedService.getForm(formName).pipe(map((model) => this.initialiseFormState(model, null)));
}
/*
Invokes the form save in the injected service (see constructor for service)
*/
saveForm(name, form) {
// remove all transactional data
const updateForm = HelperService.deepCopy(form);
updateForm.pages.forEach((page) => {
page.sections.forEach((section) => {
section.components.forEach((component) => {
Object.keys(component)
.filter((key) => component[key] === null)
.forEach((key) => delete component[key]);
delete component.value;
if (component.rules)
if (Object.keys(component.rules).length === 0 && component.rules.constructor === Object)
delete component.rules;
else
Object.keys(component.rules).forEach((p) => {
delete component.rules[p].value;
});
});
});
});
return this.injectedService.saveForm(name, updateForm).pipe(map((response) => {
return response;
}));
}
/*
Invokes the data save in the injected service (see constructor for service)
*/
saveData(dataSource, ids, data) {
return this.injectedService.saveData(dataSource, ids, data).pipe(map((result) => {
return result;
}));
}
/*
Updates a component value and recalculates all dependents
If reset is set to true, it will recalculate all dependents, this is when a rules as been
modified in the FormQL Editor
*/
updateComponent(component, formState, reset = false) {
const value = HelperService.resolveType(component.value, component.type);
formState.data = HelperService.setValue(component.schema, value, formState.data);
if (reset)
this.resetComponentDependents(formState);
// refresh any dependent components
if (component.dependents)
component.dependents.forEach((key) => {
formState.components[key] = this.resolveComponentRules(formState.components[key], formState);
});
// set the value on any components that have the same schema
Object.keys(formState.components).forEach((key) => {
const c = formState.components[key];
if (c.schema === component.schema)
try {
c.value = HelperService.getValue(c.schema, formState.data, c.type);
}
catch (err) {
throw err;
}
});
return formState;
}
/*
Initialises Form State
*/
initialiseFormState(form, data) {
const reactiveFormStructure = HelperService.createReactiveFormStructure(form, true, data);
const formState = {
components: reactiveFormStructure.components,
data: Object.assign({}, reactiveFormStructure.data),
form: form,
formControls: reactiveFormStructure.formControls,
reactiveForm: this.formBuilder.group(reactiveFormStructure.pageGroup)
};
return this.resolveConditions(formState);
}
/*
Resets all component dependents, it should only be called when a user modified a question in the formql editor
*/
resetComponentDependents(formState) {
if (formState.components) {
Object.keys(formState.components).forEach((componentKey) => delete formState.components[componentKey].dependents);
Object.keys(formState.components).forEach((componentKey) => {
const component = formState.components[componentKey];
if (component.rules)
Object.keys(component.rules).forEach((ruleKey) => {
const rule = component.rules[ruleKey];
this.ruleLogic.resetDependancies(formState, rule.condition, component);
});
});
Object.keys(formState.components).forEach((componentKey) => (formState.components[componentKey] = this.resolveComponentRules(formState.components[componentKey], formState)));
}
}
getData(query, ids) {
return this.injectedService.getData(query, ids).pipe(map((data) => {
if (data)
return data;
else
return {};
}));
}
getForms() {
return this.injectedService.getForms().pipe(map((data) => {
return data;
}));
}
getForm(name) {
return this.injectedService.getForm(name).pipe(map((data) => {
return data;
}));
}
/*
Resolve all rules for any given component
*/
resolveComponentRules(component, formState) {
if (component.rules) {
let resetValidator = false;
Object.keys(component.rules).forEach((key) => {
const property = component.rules[key];
if (property.condition) {
resetValidator = true;
const evaluatedValue = this.ruleLogic.evaluate(formState.data, property.condition);
if (key === 'value') {
const value = HelperService.resolveType(evaluatedValue, component.type);
if (component.value !== value) {
component.value = value;
formState = this.updateComponent(component, formState);
}
}
property.value = evaluatedValue;
}
else
delete component.rules[key];
});
if (resetValidator)
formState.formControls[component.componentId] = HelperService.setValidators(this.componentResolverService, component, formState.formControls[component.componentId]);
}
return component;
}
/*
Resolves all conditions in each component, used when initialising the form
*/
resolveConditions(formState) {
if (formState.components) {
const components = formState.components;
Object.keys(components).forEach((componentKey) => (components[componentKey] = this.resolveComponentRules(components[componentKey], formState)));
}
return formState;
}
};
FormService.ɵprov = ɵɵdefineInjectable({ factory: function FormService_Factory() { return new FormService(ɵɵinject("FormQLService"), ɵɵinject(ComponentResolverService), ɵɵinject(FormBuilder)); }, token: FormService, providedIn: "root" });
FormService = __decorate([
Injectable({
providedIn: 'root'
}),
__param(0, Inject('FormQLService')),
__metadata("design:paramtypes", [Object, ComponentResolverService,
FormBuilder])
], FormService);
let StoreService = class StoreService {
constructor(formService, componentResolverService, formBuilder) {
this.formService = formService;
this.componentResolverService = componentResolverService;
this.formBuilder = formBuilder;
this.data$ = new Subject();
this.formState$ = new Subject();
this.serviceDestroyed = new Subject();
}
// private formControls: FormControls;
ngOnDestroy() {
this.data$.complete();
this.data$.unsubscribe();
}
getData() {
return this.data$.asObservable();
}
getFormState() {
return this.formState$.asObservable();
}
updateComponent(component) {
this.formState = this.formService.updateComponent(component, this.formState, false);
this.data$.next(Object.assign({}, this.formState.data));
this.formState$.next(Object.assign({}, this.formState));
}
setComponent(component) {
this.formState = this.formService.updateComponent(component, this.formState, true);
// this.formControls = HelperService.resetValidators(this.formState.components, this.formControls, this.componentResolverService);
this.data$.next(Object.assign({}, this.formState.data));
this.formState$.next(Object.assign({}, this.formState));
}
getAll(formName, ids, mode) {
this.formService.getFormAndData(formName, ids).pipe(takeUntil(this.serviceDestroyed)).subscribe(response => {
this.formState = Object.assign({}, response);
this.formState.ids = ids;
this.formState.mode = mode;
this.data$.next(Object.assign({}, response.data));
this.formState$.next(this.formState);
}, error => {
this.formState$.next({
form: {
error: HelperService.formatError({
title: 'Error loading form or data',
error: error
})
}
});
});
}
saveForm() {
this.formService.saveForm(this.formState.form.formName, this.formState.form);
}
saveData() {
return this.formService.saveData(this.formState.form.dataSource, this.formState.ids, this.formState.data);
}
validateForm() {
HelperService.validateForm(this.formState.reactiveForm);
}
isFormValid() {
return this.formState.reactiveForm.valid;
}
unsubscribeAll() {
this.serviceDestroyed.next();
this.serviceDestroyed.complete();
}
reSetForm(eventType, event) {
switch (eventType) {
case InternalEventType.EditingForm:
this.populateReactiveForm();
break;
case InternalEventType.DndFormChanged:
const pageId = event.pageId;
const indexDnd = this.formState.form.pages.findIndex(p => p.pageId === pageId);
if (indexDnd >= 0)
this.formState.form.pages[indexDnd] = event;
this.populateReactiveForm();
break;
case InternalEventType.RemoveComponent:
const componentId = event.componentId;
let updateSectionId = '';
this.formState.form.pages.forEach(page => {
page.sections.forEach(section => {
const indexComponent = section.components.findIndex(c => c.componentId === componentId);
if (indexComponent >= 0) {
section.components.splice(indexComponent, 1);
updateSectionId = section.sectionId;
}
});
});
this.populateReactiveForm();
break;
case InternalEventType.RemoveSection:
const sectionId = event.sectionId;
let updatePageId = '';
this.formState.form.pages.forEach(page => {
const indexSection = page.sections.findIndex(c => c.sectionId === sectionId);
if (indexSection >= 0) {
page.sections.splice(indexSection, 1);
updatePageId = page.pageId;
}
});
this.populateReactiveForm();
break;
}
this.formState$.next(Object.assign({}, this.formState));
}
populateReactiveForm() {
if (this.formState.form.pages != null && this.formState.form.pages.length > 0) {
// get reactive structure -> formControls, pageGroup and components if it's an update
const reactiveFormStructure = HelperService.createReactiveFormStructure(this.formState.form, true, this.formState.data);
this.formState.formControls = reactiveFormStructure.formControls;
// if it's an update, refresh reactive form, set all form controls, validators
this.formState.form.pages.forEach(page => {
this.formState.reactiveForm.setControl(page.pageId, reactiveFormStructure.pageGroup[page.pageId]);
});
this.formState.form = HelperService.updateTemplates(this.formState.form);
if (reactiveFormStructure.components != null && Object.keys(reactiveFormStructure.components).length > 0)
this.formState.formControls = HelperService.resetValidators(reactiveFormStructure.components, this.formState.formControls, this.componentResolverService);
this.formState = this.formService.resolveConditions(this.formState);
}
}
};
StoreService.ɵprov = ɵɵdefineInjectable({ factory: function StoreService_Factory() { return new StoreService(ɵɵinject(FormService), ɵɵinject(ComponentResolverService), ɵɵinject(FormBuilder)); }, token: StoreService, providedIn: "root" });
StoreService = __decorate([
Injectable({ providedIn: 'root' }),
__metadata("design:paramtypes", [FormService,
ComponentResolverService,
FormBuilder])
], StoreService);
let PageWrapperComponent = class PageWrapperComponent {
constructor(dndService, storeService) {
this.dndService = dndService;
this.storeService = storeService;
this.FormQLMode = FormQLMode;
this.ContainerType = ContainerType;
this.ComponentPositionType = GridPositionType;
}
ngOnInit() {
this.sections = this.createSections(this.page);
}
synchroniseModel($event, positionId) {
const dndEvent = {
sourceObjectId: $event.sourceObjectId,
sourceWrapperId: $event.sourceWrapperId,
targetPositionId: positionId,
targetWrapperId: this.page.pageId,
targetIndexId: $event.targetIndexId
};
this.page = this.dndService.synchronisePageModel(this.page, dndEvent);
this.sections = this.createSections(this.page);
this.storeService.reSetForm(InternalEventType.DndFormChanged, this.page);
}
createSections(page) {
const sections = {};
if (page && page.sections)
page.sections.forEach(section => {
if (sections && sections[section.position.id])
sections[section.position.id].push(section);
else
sections[section.position.id] = [section];
});
return sections;
}
trackByFn(index, item) {
return item.id;
}
resetSections() {
if (this.sections)
this.sections = this.createSections(this.page);
}
};
__decorate([
Input(),
__metadata("design:type", Object)
], PageWrapperComponent.prototype, "page", void 0);
__decorate([
Input(),
__metadata("design:type", FormGroup)
], PageWrapperComponent.prototype, "reactivePage", void 0);
__decorate([
Input(),
__metadata("design:type", Number)
], PageWrapperComponent.prototype, "mode", void 0);
PageWrapperComponent = __decorate([
Component({
// tslint:disable-next-line: component-selector
selector: '[formql-page-wrapper]',
template: `
<div class="fql-page-body">
<ng-template formqlGdConfig
[formqlGdConfigOf]="page.template.body" let-bodyitem let-i="index"
(resetItems)="resetSections()">
<div
[ngClass]="{'fql-page-container': mode === FormQLMode.Edit}"
[ngStyle]="bodyitem.style">
<div formqlDndDrop
[type]="ContainerType.Section"
[mode]="mode"
[ngClass]="{'fql-page-container': (mode === FormQLMode.Edit)}"
(synchronise)="synchroniseModel($event, bodyitem.id)">
<ng-container *ngFor="let section of sections[bodyitem.id]; trackBy: trackByFn">
<ng-container *ngTemplateOutlet="templateRef; context: {section: section}">
</ng-container>
</ng-container>
</div>
</div>
</ng-template>
</div>
<ng-template #templateRef let-section="section">
<div [formGroup]="reactivePage">
<div formql-section-wrapper
[page]="page"
[section]="section"
[formGroupName]="section.sectionId"
[reactiveSection]="reactivePage.controls[section.sectionId]"
[mode]="mode">
</div>
</div>
</ng-template>`,
providers: [DndService],
styles: [".fql-bundle-field-input{width:100%;box-sizing:border-box;-webkit-box-sizing:border-box;-moz-box-sizing:border-box}.fql-bundle-checkbox-input{cursor:pointer;box-sizing:border-box;-webkit-box-sizing:border-box;-moz-box-sizing:border-box}.fql-bundle-label-required:after{content:\" *\";color:red}.fql-bundle-field-radio{cursor:pointer}.fql-error-message{text-align:center;padding:20px}.fql-dnd-container-separator{box-shadow:0 -2px 0 #00f}.fql-dnd-container-drop-area{outline:dashed 3px}.fql-page-body{display:-ms-grid;display:grid;width:100%;min-height:30px}.fql-page-container{display:table;height:100%;width:100%;min-height:15px}.fql-page-container:hover{outline:solid 2px}"]
}),
__metadata("design:paramtypes", [DndService,
StoreService])
], PageWrapperComponent);
let InternalEventHandlerService = class InternalEventHandlerService {
constructor() {
this.event = new EventEmitter();
}
send(eventType, event) {
const eventHandler = {
event: event,
eventType: eventType
};
this.event.emit(eventHandler);
}
};
InternalEventHandlerService.ɵprov = ɵɵdefineInjectable({ factory: function InternalEventHandlerService_Factory() { return new InternalEventHandlerService(); }, token: InternalEventHandlerService, providedIn: "root" });
__decorate([
Output(),
__metadata("design:type", EventEmitter)
], InternalEventHandlerService.prototype, "event", void 0);
InternalEventHandlerService = __decorate([
Directive(),
Injectable({
providedIn: 'root'
})
], InternalEventHandlerService);
let SectionWrapperComponent = class SectionWrapperComponent {
constructor(internalEventHandlerService, componentResolverService, viewContainerRef, dndService, storeService) {
this.internalEventHandlerService = internalEventHandlerService;
this.componentResolverService = componentResolverService;
this.viewContainerRef = viewContainerRef;
this.dndService = dndService;
this.storeService = storeService;
this.FormQLMode = FormQLMode;
this.ContainerType = ContainerType;
this.ComponentPositionType = GridPositionType;
}
ngOnInit() {
this.components = this.createComponents(this.section);
if (this.mode === FormQLMode.Edit) {
const tooltip = this.viewContainerRef.createComponent(this.componentResolverService.resolveComponent('TooltipComponent'));
tooltip.instance.wrapper = this.wrapper;
tooltip.instance.type = ContainerType.Section;
tooltip.instance.object = this.section;
this.tooltip.insert(tooltip.hostView);
}
}
editField() {
if (this.mode === FormQLMode.Edit)
this.internalEventHandlerService.send(InternalEventType.EditingSection, this.section);
}
synchroniseModel($event, positionId, positionType) {
const dndEvent = {
sourceObjectId: $event.sourceObjectId,
sourceWrapperId: $event.sourceWrapperId,
targetPositionId: positionId,
targetWrapperId: this.section.sectionId,
targetIndexId: $event.targetIndexId,
positionType: positionType
};
this.page = this.dndService.synchroniseSectionModel(this.page, dndEvent);
this.storeService.reSetForm(InternalEventType.DndFormChanged, this.page);
}
trackByFn(index, item) {
return item.id;
}
createComponents(section) {
const components = {};
if (section && section.components)
section.components.sort((left, right) => {
return left.position.index - right.position.index;
});
section.components.forEach(component => {
if (components && components[component.position.type + '_' + component.position.id])
components[component.position.type + '_' + component.position.id].push(component);
else
components[component.position.type + '_' + component.position.id] = [component];
});
return components;
}
};
__decorate([
ViewChild('wrapper', { read: ViewContainerRef, static: true }),
__metadata("design:type", ViewContainerRef)
], SectionWrapperComponent.prototype, "wrapper", void 0);
__decorate([
ViewChild('tooltip', { read: ViewContainerRef, static: true }),
__metadata("design:type", ViewContainerRef)
], SectionWrapperComponent.prototype, "tooltip", void 0);
__decorate([
Input(),
__metadata("design:type", Object)
], SectionWrapperComponent.prototype, "section", void 0);
__decorate([
Input(),
__metadata("design:type", FormGroup)
], SectionWrapperComponent.prototype, "reactiveSection", void 0);
__decorate([
Input(),
__metadata("design:type", Object)
], SectionWrapperComponent.prototype, "page", void 0);
__decorate([
Input(),
__metadata("design:type", Number)
], SectionWrapperComponent.prototype, "mode", void 0);
SectionWrapperComponent = __decorate([
Component({
// tslint:disable-next-line: component-selector
selector: '[formql-section-wrapper]',
template: `
<div #wrapper for