Skip to content

Commit

Permalink
Merge pull request #190 from formio/fio-9267-9268-removed-skipping--p…
Browse files Browse the repository at this point in the history
…rocessing-if-there-is-no-data

FIO-9266,FIO-9267,FIO-9268: Fixes an issue where nested forms will be not validated if there are no data provided
  • Loading branch information
lane-formio authored Nov 15, 2024
2 parents 4cc23fc + a34e0e0 commit 555beb3
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 17 deletions.
129 changes: 129 additions & 0 deletions src/process/__tests__/process.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4548,6 +4548,135 @@ describe('Process Tests', function () {
});
});

it("Should validate Nested Form's components if it should not be cleared and no data provided", async function () {
const nestedForm = {
key: 'form',
type: 'form',
display: 'form',
input: true,
components: [
{
key: 'textField',
type: 'textfield',
validate: {
required: true,
},
input: true,
},
],
};
const submission = {
data: {
submit: true,
},
state: 'submitted',
};
const form = {
title: 'Parent Form',
name: 'parentForm',
path: 'parentform',
type: 'form',
display: 'form',
components: [
nestedForm,
{
...nestedForm,
key: 'form1',
},
{
type: 'button',
label: 'Submit',
key: 'submit',
input: true,
tableView: false,
},
],
};
const context = {
form,
submission,
data: submission.data,
components: form.components,
processors: ProcessTargets.submission,
scope: {},
config: {
server: true,
},
};
processSync(context);
context.processors = ProcessTargets.evaluator;
const scope = processSync(context);
expect((scope as ValidationScope).errors).to.have.length(2);
});

it("Should validate Nested Form's components if it should not be cleared and no data provided and the parent form has errors itself", async function () {
const nestedForm = {
key: 'form',
type: 'form',
input: true,
components: [
{
key: 'textField',
type: 'textfield',
validate: {
required: true,
},
input: true,
},
],
};
const submission = {
data: {
submit: true,
},
state: 'submitted',
};
const form = {
title: 'Parent Form',
name: 'parentForm',
path: 'parentform',
type: 'form',
display: 'form',
components: [
{
key: 'textField',
type: 'textfield',
validate: {
required: true,
},
input: true,
},
nestedForm,
{
...nestedForm,
key: 'form1',
},
{
type: 'button',
label: 'Submit',
key: 'submit',
input: true,
tableView: false,
},
],
};
const context = {
form,
submission,
data: submission.data,
components: form.components,
processors: ProcessTargets.submission,
scope: {},
config: {
server: true,
},
};
processSync(context);
context.processors = ProcessTargets.evaluator;
const scope = processSync(context);
expect((scope as ValidationScope).errors).to.have.length(3);
});

it('Should not return errors for empty multiple values for url and dateTime', function () {
const form = {
_id: '671f7fbeaf87b0e2a26e4212',
Expand Down
18 changes: 10 additions & 8 deletions src/utils/formUtil/eachComponentData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const eachComponentData = (
parent?: Component,
includeAll: boolean = false,
) => {
if (!components || !data) {
if (!components) {
return;
}
return eachComponent(
Expand Down Expand Up @@ -61,14 +61,16 @@ export const eachComponentData = (
return true;
}
if (getModelType(component) === 'dataObject') {
// No need to bother processing all the children data if there is no data for this form or the reference value has not been loaded.
const nestedFormValue: any = get(data, component.path);
const noReferenceAttached =
nestedFormValue?._id && isEmpty(nestedFormValue.data) && !has(nestedFormValue, 'form');
const shouldProcessNestedFormData = nestedFormValue?._id
? !noReferenceAttached
: has(data, component.path);
if (shouldProcessNestedFormData) {
const noReferenceAttached = nestedFormValue?._id
? isEmpty(nestedFormValue.data) && !has(nestedFormValue, 'form')
: false;
const shouldBeCleared =
(!component.hasOwnProperty('clearOnHide') || component.clearOnHide) &&
(component.hidden || component.ephemeralState?.conditionallyHidden);
// Skip all the nested components processing if nested form is hidden and should be cleared on hide or if submission is saved as reference and not loaded
const shouldSkipProcessingNestedFormData = noReferenceAttached || shouldBeCleared;
if (!shouldSkipProcessingNestedFormData) {
// For nested forms, we need to reset the "data" and "path" objects for all of the children components, and then re-establish the data when it is done.
const childPath: string = componentDataPath(component, path, compPath);
const childData: any = get(data, childPath, {});
Expand Down
20 changes: 11 additions & 9 deletions src/utils/formUtil/eachComponentDataAsync.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { get, set, has, isEmpty } from 'lodash';
import { get, set, isEmpty, has } from 'lodash';

import {
Component,
Expand Down Expand Up @@ -28,7 +28,7 @@ export const eachComponentDataAsync = async (
parent?: Component,
includeAll: boolean = false,
) => {
if (!components || !data) {
if (!components) {
return;
}
return await eachComponentAsync(
Expand Down Expand Up @@ -64,14 +64,16 @@ export const eachComponentDataAsync = async (
return true;
}
if (getModelType(component) === 'dataObject') {
// No need to bother processing all the children data if there is no data for this form or the reference value has not been loaded.
const nestedFormValue: any = get(data, component.path);
const noReferenceAttached =
nestedFormValue?._id && isEmpty(nestedFormValue.data) && !has(nestedFormValue, 'form');
const shouldProcessNestedFormData = nestedFormValue?._id
? !noReferenceAttached
: has(data, component.path);
if (shouldProcessNestedFormData) {
const noReferenceAttached = nestedFormValue?._id
? isEmpty(nestedFormValue.data) && !has(nestedFormValue, 'form')
: false;
const shouldBeCleared =
(!component.hasOwnProperty('clearOnHide') || component.clearOnHide) &&
(component.hidden || component.ephemeralState?.conditionallyHidden);
// Skip all the nested components processing if nested form is hidden and should be cleared on hide or if submission is saved as reference and not loaded
const shouldSkipProcessingNestedFormData = noReferenceAttached || shouldBeCleared;
if (!shouldSkipProcessingNestedFormData) {
// For nested forms, we need to reset the "data" and "path" objects for all of the children components, and then re-establish the data when it is done.
const childPath: string = componentDataPath(component, path, compPath);
const childData: any = get(data, childPath, null);
Expand Down

0 comments on commit 555beb3

Please sign in to comment.