diff --git a/packages/form-js-viewer/src/core/FormFieldInstanceRegistry.js b/packages/form-js-viewer/src/core/FormFieldInstanceRegistry.js new file mode 100644 index 000000000..0b809f311 --- /dev/null +++ b/packages/form-js-viewer/src/core/FormFieldInstanceRegistry.js @@ -0,0 +1,66 @@ +export class FormFieldInstanceRegistry { + constructor(eventBus, formFieldRegistry, formFields) { + this._eventBus = eventBus; + this._formFieldRegistry = formFieldRegistry; + this._formFields = formFields; + + this._formFieldInstances = {}; + + eventBus.on('form.clear', () => this.clear()); + } + + add(instance) { + + const { + id, + expressionContextInfo, + valuePath, + indexes + } = instance; + + const instanceId = [ id, ...Object.values(indexes || {}) ].join('_'); + + if (this._formFieldInstances[ instanceId ]) { + throw new Error('this form field instance is already registered'); + } + + this._formFieldInstances[ instanceId ] = { + id, + instanceId, + expressionContextInfo, + valuePath, + indexes + }; + + return instanceId; + } + + remove(instanceId) { + + if (!this._formFieldInstances[ instanceId ]) { + return; + } + + delete this._formFieldInstances[ instanceId ]; + } + + getAll() { + return Object.values(this._formFieldInstances); + } + + getAllKeyed() { + return this.getAll().filter(({ id }) => { + const { type } = this._formFieldRegistry.get(id); + const { config } = this._formFields.get(type); + + return config.keyed; + }); + } + + clear() { + this._formFieldInstances = {}; + } + +} + +FormFieldInstanceRegistry.$inject = [ 'eventBus', 'formFieldRegistry', 'formFields' ]; \ No newline at end of file diff --git a/packages/form-js-viewer/src/core/index.js b/packages/form-js-viewer/src/core/index.js index 04fb4cfce..4dd2a0511 100644 --- a/packages/form-js-viewer/src/core/index.js +++ b/packages/form-js-viewer/src/core/index.js @@ -5,6 +5,7 @@ import { FieldFactory } from './FieldFactory'; import { PathRegistry } from './PathRegistry'; import { FormLayouter } from './FormLayouter'; import { FormFieldRegistry } from './FormFieldRegistry'; +import { FormFieldInstanceRegistry } from './FormFieldInstanceRegistry'; import { RenderModule } from '../render'; @@ -16,6 +17,7 @@ export const CoreModule = { importer: [ 'type', Importer ], fieldFactory: [ 'type', FieldFactory ], formFieldRegistry: [ 'type', FormFieldRegistry ], + formFieldInstanceRegistry: [ 'type', FormFieldInstanceRegistry ], pathRegistry: [ 'type', PathRegistry ], formLayouter: [ 'type', FormLayouter ], validator: [ 'type', Validator ] diff --git a/packages/form-js-viewer/src/render/components/FormField.js b/packages/form-js-viewer/src/render/components/FormField.js index 7033d1b90..4714168ee 100644 --- a/packages/form-js-viewer/src/render/components/FormField.js +++ b/packages/form-js-viewer/src/render/components/FormField.js @@ -3,7 +3,7 @@ import isEqual from 'lodash/isEqual'; import { get } from 'min-dash'; -import { FormContext, FormRenderContext } from '../context'; +import { FormContext, FormRenderContext, LocalExpressionContext } from '../context'; import { useCondition, @@ -25,6 +25,7 @@ export function FormField(props) { const formFields = useService('formFields'), viewerCommands = useService('viewerCommands', false), + formFieldInstanceRegistry = useService('formFieldInstanceRegistry', false), pathRegistry = useService('pathRegistry'), eventBus = useService('eventBus'), form = useService('form'); @@ -56,6 +57,7 @@ export function FormField(props) { const fieldConfig = FormFieldComponent.config; + const localExpressionContext = useContext(LocalExpressionContext); const valuePath = useMemo(() => pathRegistry.getValuePath(field, { indexes }), [ field, indexes, pathRegistry ]); const initialValue = useMemo(() => get(initialData, valuePath), [ initialData, valuePath ]); @@ -69,6 +71,22 @@ export function FormField(props) { properties.disabled || field.disabled || false ); + // register form field instance + useEffect(() => { + if (formFieldInstanceRegistry) { + const instanceId = formFieldInstanceRegistry.add({ + id: field.id, + expressionContextInfo: localExpressionContext, + valuePath, + indexes + }); + + return () => { + formFieldInstanceRegistry.remove(instanceId); + }; + } + }, [ formFieldInstanceRegistry, field.id, localExpressionContext, valuePath, indexes ]); + // ensures the initial validation behavior can be re-triggered upon form reset useEffect(() => {