diff --git a/ui/src/app/core/components/schema-form/schema-form.component.html b/ui/src/app/core/components/schema-form/schema-form.component.html index dd6255338..ff6a16005 100644 --- a/ui/src/app/core/components/schema-form/schema-form.component.html +++ b/ui/src/app/core/components/schema-form/schema-form.component.html @@ -1,6 +1,6 @@ - - \ No newline at end of file + diff --git a/ui/src/app/core/core.module.ts b/ui/src/app/core/core.module.ts index d2d25610b..7b08bffa0 100644 --- a/ui/src/app/core/core.module.ts +++ b/ui/src/app/core/core.module.ts @@ -15,6 +15,7 @@ import { ConfirmComponent } from './components/confirm/confirm.component'; import { SchemaFormComponent } from './components/schema-form/schema-form.component'; import { QrcodeComponent } from './components/qrcode/qrcode.component'; import { RtlDirective } from './directives/rtl.directive'; +import { JsonSchemaFormPatchDirective } from './directives/json-schema-form-patch.directive'; @NgModule({ declarations: [ @@ -26,6 +27,7 @@ import { RtlDirective } from './directives/rtl.directive'; HrefTargetBlankDirective, LongClickDirective, RtlDirective, + JsonSchemaFormPatchDirective, BackupRestoreComponent, ScheduledBackupsComponent, ConfirmComponent, @@ -47,6 +49,7 @@ import { RtlDirective } from './directives/rtl.directive'; HrefTargetBlankDirective, LongClickDirective, RtlDirective, + JsonSchemaFormPatchDirective, ], }) export class CoreModule { } diff --git a/ui/src/app/core/directives/json-schema-form-patch.directive.ts b/ui/src/app/core/directives/json-schema-form-patch.directive.ts new file mode 100644 index 000000000..c17937d45 --- /dev/null +++ b/ui/src/app/core/directives/json-schema-form-patch.directive.ts @@ -0,0 +1,97 @@ +import {Directive, Host, Input, Optional, Self} from '@angular/core'; +import {JsonSchemaFormComponent} from '@oznu/ngx-bs4-jsonform'; +import {cloneDeep, merge, uniqueId} from 'lodash-es'; + +@Directive({ + selector: '[jsfPatch]', +}) +export class JsonSchemaFormPatchDirective { + @Input() jsfPatch = false; + + constructor( + @Host() @Self() @Optional() public jsonSchemaForm: JsonSchemaFormComponent) { + + let buildLayout_original = jsonSchemaForm.jsf.buildLayout.bind(jsonSchemaForm.jsf) + + jsonSchemaForm.jsf.buildLayout = (widgetLibrary: any) => { + + buildLayout_original(widgetLibrary); + if (jsonSchemaForm.jsf.formValues && this.jsfPatch) { + return this.fixNestedArrayLayout( + jsonSchemaForm.jsf.layout, + jsonSchemaForm.jsf.formValues + ); + } + } + + } + + private fixNestedArrayLayout(builtLayout: any[], formData: any) { + this.fixArray(builtLayout, formData, ''); + return builtLayout; + } + + private fixArray(items: any | any[], formData: any, refPointer: string) { + + if (Array.isArray(items)) { + items.filter(x => x.name !== "_bridge" && (x.dataType == "array" || x.arrayItem)).forEach(item => { + this.fixNestedArray(item, formData, refPointer); + }) + } else { + this.fixNestedArray(items, formData, refPointer); + } + } + + private fixNestedArray(item: any, formData: any, refPointer: string) { + if (item.items && Array.isArray(item.items)) { + + const ref = item.items.find(x => x.type === "$ref"); + if (ref) { + const dataItems = item.items.filter(x => x.type === "section"); + const template = dataItems.length > 0 + ? dataItems.reduce((a, b) => a.id > b.id ? a : b) + : this.getItemTemplateFromRef(ref); + + const data = this.getDataFromPointer(formData, ref.dataPointer.replace(refPointer, '')); + + if (Array.isArray(data)) { + // add missing items + while (item.items.length - 1 < data.length) { + const newItem = cloneDeep(template); + newItem._id = uniqueId("new_"); + + item.items.unshift(newItem); + } + + data.forEach((d: any, index: number) => { + this.fixArray(item.items[index], d, ref.dataPointer); + }); + } else { + this.fixArray(item.items, formData, ref.dataPointer); + } + } else { + this.fixArray(item.items, formData, refPointer); + } + } + } + + private getDataFromPointer(data: any, dataPointer: string) { + let value = data; + + dataPointer.substring(1).split(/\//).filter(x => x !== '-') + .forEach((key: string) => value = value[key]); + + return value; + } + + private getItemTemplateFromRef(ref: any) { + const templateNode = { + "type": "section", + "items": [] + }; + + const item = cloneDeep(ref); + merge(item, templateNode); + return item; + } +}