Skip to content

Commit c9e3e7b

Browse files
committed
feat: use field instance validation in formfields
Closes #1147
1 parent f307738 commit c9e3e7b

File tree

6 files changed

+90
-48
lines changed

6 files changed

+90
-48
lines changed

packages/form-js-viewer/src/features/viewerCommands/ViewerCommands.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { UpdateFieldValidationHandler } from './cmd/UpdateFieldValidationHandler';
2+
import { UpdateFieldInstanceValidationHandler } from './cmd/UpdateFieldInstanceValidationHandler';
23

34
export class ViewerCommands {
45
constructor(commandStack, eventBus) {
@@ -18,9 +19,13 @@ export class ViewerCommands {
1819
getHandlers() {
1920
return {
2021
'formField.validation.update': UpdateFieldValidationHandler,
22+
'formFieldInstance.validation.update': UpdateFieldInstanceValidationHandler,
2123
};
2224
}
2325

26+
/**
27+
* @deprecated
28+
*/
2429
updateFieldValidation(field, value, indexes) {
2530
const context = {
2631
field,
@@ -30,6 +35,15 @@ export class ViewerCommands {
3035

3136
this._commandStack.execute('formField.validation.update', context);
3237
}
38+
39+
updateFieldInstanceValidation(fieldInstance, value) {
40+
const context = {
41+
fieldInstance,
42+
value,
43+
};
44+
45+
this._commandStack.execute('formFieldInstance.validation.update', context);
46+
}
3347
}
3448

3549
ViewerCommands.$inject = ['commandStack', 'eventBus'];
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { set } from 'min-dash';
2+
import { clone } from '../../../util';
3+
4+
export class UpdateFieldInstanceValidationHandler {
5+
constructor(form, validator) {
6+
this._form = form;
7+
this._validator = validator;
8+
}
9+
10+
execute(context) {
11+
const { fieldInstance, value } = context;
12+
const { id, indexes } = fieldInstance;
13+
const { errors } = this._form._getState();
14+
15+
context.oldErrors = clone(errors);
16+
17+
const fieldErrors = this._validator.validateFieldInstance(fieldInstance, value);
18+
const updatedErrors = set(
19+
errors,
20+
[id, ...Object.values(indexes || {})],
21+
fieldErrors.length ? fieldErrors : undefined,
22+
);
23+
this._form._setState({ errors: updatedErrors });
24+
}
25+
26+
revert(context) {
27+
this._form._setState({ errors: context.oldErrors });
28+
}
29+
}
30+
31+
UpdateFieldInstanceValidationHandler.$inject = ['form', 'validator'];

packages/form-js-viewer/src/features/viewerCommands/cmd/UpdateFieldValidationHandler.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { set } from 'min-dash';
22
import { clone } from '../../../util';
33

4+
/**
5+
* @deprecated
6+
*/
47
export class UpdateFieldValidationHandler {
58
constructor(form, validator) {
69
this._form = form;

packages/form-js-viewer/src/render/components/FormField.js

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -98,35 +98,31 @@ export function FormField(props) {
9898

9999
if (initialValidationTrigger && hasInitialValue) {
100100
setInitialValidationTrigger(false);
101-
viewerCommands.updateFieldValidation(field, initialValue, indexes);
101+
viewerCommands.updateFieldInstanceValidation(fieldInstance, initialValue);
102102
}
103-
}, [viewerCommands, field, initialValue, initialValidationTrigger, indexes]);
103+
}, [fieldInstance, initialValidationTrigger, initialValue, viewerCommands]);
104104

105105
const onBlur = useCallback(() => {
106106
const value = get(data, valuePath);
107107

108108
if (initialValidationTrigger) {
109109
setInitialValidationTrigger(false);
110-
viewerCommands.updateFieldValidation(field, value, indexes);
110+
viewerCommands.updateFieldInstanceValidation(fieldInstance, value);
111111
}
112112

113113
eventBus.fire('formField.blur', { formField: field });
114-
}, [eventBus, field, indexes, viewerCommands, initialValidationTrigger, data, valuePath]);
114+
}, [data, eventBus, field, fieldInstance, initialValidationTrigger, valuePath, viewerCommands]);
115115

116116
const onFocus = useCallback(() => {
117117
eventBus.fire('formField.focus', { formField: field });
118118
}, [eventBus, field]);
119119

120120
const onChange = useCallback(
121121
(update) => {
122-
if (!fieldConfig.keyed) {
123-
return;
124-
}
125-
126122
setInitialValidationTrigger(false);
127-
_onChange({ ...update, field, indexes, fieldInstance });
123+
_onChange({ field, indexes, fieldInstance, ...update });
128124
},
129-
[_onChange, field, fieldConfig.keyed, fieldInstance, indexes],
125+
[_onChange, field, fieldInstance, indexes],
130126
);
131127

132128
if (hidden) {
@@ -147,6 +143,7 @@ export function FormField(props) {
147143
onFocus={disabled || readonly ? noop : onFocus}
148144
readonly={readonly}
149145
value={value}
146+
fieldInstance={fieldInstance}
150147
/>
151148
);
152149

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,34 @@
1-
import { wrapObjectKeysWithUnderscores } from './simple';
2-
3-
4-
/**
5-
* Transform a LocalExpressionContext object into a usable FEEL context.
6-
*
7-
* @param {Object} context - The LocalExpressionContext object.
8-
* @returns {Object} The usable FEEL context.
9-
*/
10-
11-
export function buildExpressionContext(context) {
12-
const {
13-
data,
14-
...specialContextKeys
15-
} = context;
16-
17-
return {
18-
...specialContextKeys,
19-
...data,
20-
...wrapObjectKeysWithUnderscores(specialContextKeys)
21-
};
22-
}
23-
24-
/**
25-
* Evaluate a string based on the expressionLanguage and context information.
26-
* If the string is not an expression, it is returned as is.
27-
*
28-
* @param {any} expressionLanguage - The expression language to use.
29-
* @param {string} value - The string to evaluate.
30-
* @param {Object} expressionContextInfo - The context information to use.
31-
* @returns {any} - Evaluated value or the original value if not an expression.
32-
*/
33-
export function runExpressionEvaluation(expressionLanguage, value, expressionContextInfo) {
34-
if (expressionLanguage && expressionLanguage.isExpression(value)) {
35-
return expressionLanguage.evaluate(value, buildExpressionContext(expressionContextInfo));
36-
}
37-
return value;
38-
}
1+
import { wrapObjectKeysWithUnderscores } from './simple';
2+
3+
/**
4+
* Transform a LocalExpressionContext object into a usable FEEL context.
5+
*
6+
* @param {Object} context - The LocalExpressionContext object.
7+
* @returns {Object} The usable FEEL context.
8+
*/
9+
10+
export function buildExpressionContext(context) {
11+
const { data, ...specialContextKeys } = context;
12+
13+
return {
14+
...specialContextKeys,
15+
...data,
16+
...wrapObjectKeysWithUnderscores(specialContextKeys),
17+
};
18+
}
19+
20+
/**
21+
* Evaluate a string based on the expressionLanguage and context information.
22+
* If the string is not an expression, it is returned as is.
23+
*
24+
* @param {any} expressionLanguage - The expression language to use.
25+
* @param {string} value - The string to evaluate.
26+
* @param {Object} expressionContextInfo - The context information to use.
27+
* @returns {any} - Evaluated value or the original value if not an expression.
28+
*/
29+
export function runExpressionEvaluation(expressionLanguage, value, expressionContextInfo) {
30+
if (expressionLanguage && expressionLanguage.isExpression(value)) {
31+
return expressionLanguage.evaluate(value, buildExpressionContext(expressionContextInfo));
32+
}
33+
return value;
34+
}

packages/form-js-viewer/test/spec/render/components/helper/mocks/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,5 @@ export class ExpressionLanguageMock {
147147

148148
export class ViewerCommandsMock {
149149
updateFieldValidation() {}
150+
updateFieldInstanceValidation() {}
150151
}

0 commit comments

Comments
 (0)