[Bug] Error on save button after drag drop field
Describe the bug When a text field component is dragged and dropped, the popup for details open. when you click save it throws error and breaks, stop working
Here is the error,
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'hasExtraPages') at Webform.validationProcessor (NestedComponent.js:688:1) at Object.validationProcessorProcess (NestedComponent.js:715:1) at processOneSync (processOne.js:86:1) at process.js:64:1 at eachComponentData.js:14:1 at eachComponent.js:53:1 at Array.forEach (<anonymous>) at eachComponent (eachComponent.js:22:1) at eachComponentData (eachComponentData.js:11:1) at processSync (process.js:59:1)
The exact line of code from where error origins, /lib/cjs/components/_classes/nested/NestedComponent.js
if (this.root.hasExtraPages && this.page !== this.root.page) {
my code for UI,
return ( <FormioProvider> <Paper sx={{ height: 400, width: '100%' }}> <DataGrid rows={rows} columns={columns} columnVisibilityModel={{ formTypeID: false, StructuredJson: false, status: false, }} initialState={{
pagination: {
paginationModel: {
pageSize: 5,
},
},
}}
pageSizeOptions={[5]}
checkboxSelection
disableRowSelectionOnClick
/>
<Box sx={{ mb: 2, p: 2, border: '1px solid #ccc', borderRadius: '4px' }}>
<Typography variant="h6" gutterBottom>
Configure DataGrid
</Typography>
<Box component="form" noValidate autoComplete="off">
<TextField
label="Form Name"
value={selectedForm?.Name || ''}
onChange={(e) => {
setSelectedForm((prev) => ({ ...prev, Name: e.target.value }));
console.log('Name updated:', e.target.value);
}}
sx={{ mr: 2, mb: 2 }}
/>
<TextField
label="Description"
value={selectedForm?.Description || ''}
onChange={(e) => {
setSelectedForm((prev) => ({ ...prev, Description: e.target.value }));
console.log('Description updated:', e.target.value);
}}
sx={{ mr: 2, mb: 2 }}
/>
<TextField
label="Purpose"
value={selectedForm?.Purpose || ''}
onChange={(e) => {
setSelectedForm((prev) => ({ ...prev, Purpose: e.target.value }));
console.log('Purpose updated:', e.target.value);
}}
sx={{ mr: 2, mb: 2 }}
/>
<Button
variant="contained"
onClick={() => {
const updatedForm = {
...selectedForm,
StructuredJson: JSON.stringify(formDefinition), // Update with FormBuilder's JSON
};
console.log('Configuration saved:', updatedForm);
debugger;
if (!selectedForm) return;
setLoading(true);
if (selectedForm.FormTypeID>0)
{
api
.put(`FormTypes/${selectedForm.FormTypeID}`, updatedForm)
.then(() => {
alert('Configuration saved successfully!');
})
.catch((error) => {
console.error('Error saving configuration:', error);
alert('Error saving configuration. Please try again.');
});
}else{
}
alert('Configuration saved successfully!');
}}
>
Save Configuration
</Button>
</Box>
</Box>
<FormBuilder initialForm={formDefinition || { type: 'form', display: 'form', components: [] }} options={{
builder: {
ace: {
workerSrc: '/worker-json.js', // Use the local script
},
},
}}
onChange={(schema) => {
runCount++;
console.log(Run #${runCount}:, schema);
try {
if (!schema || !schema.components || schema.components.length === 0)
return;
setFormDefinition(schema);
} catch (error) {
console.error('Error updating form definition:', error);
}
}}/> </Paper> </FormioProvider> );
Version/Branch
What release version or branch are you experiencing the behavior
npm list @formio/js
├── @formio/[email protected] └─┬ @formio/[email protected] └── @formio/[email protected] deduped
To Reproduce Steps to reproduce the behavior:
- Create form...
- Add component "Text Field"
- Click Save button
- See error
Expected behavior On save the component changes should be made instead of throwing error
Screenshots
Additional context From my investigation it seems this.root is undefined which is triggering this error
Could you please give a minimally reproducible example for recreating this issue. Does this only happen when using react? Are you able to reproduce the issue without using @formio/react? A codesandbox or jsfiddle would be helpful to debug this issue
@pheeca I had the same issue while upgrading to the next version, in my case: "@formio/core": "2.3.2", "@formio/js": "5.0.1", "@formio/react": "6.0.1",
After some testing, I think that the issue is linked to unstable reference to the props passed to the FormBuilder component, especially the options props, when you pass the options directly:
(The example here is based on the first comment, normally you shouldn't pass a new formDefinition on each onChange)
function App() {
const [formDefinition, setFormDefinition] = useState();
return (
<>
<FormBuilder
initialForm={formDefinition || { type: "form", display: "form", components: [] }}
options={{
builder: {
ace: {
workerSrc: "/worker-json.js", // Use the local script
},
},
}}
onChange={(schema) => {
try {
if (!schema || !schema.components || schema.components.length === 0) return;
setFormDefinition(schema);
} catch (error) {
console.error("Error updating form definition:", error);
}
}}
/>
</>
);
}
every onChanges causes a re-render which passes a new object to the options, which internally recreates the builder instance. If your options have a stable reference, the exception doesn't happen anymore:
const options = {
builder: {
ace: {
workerSrc: "/worker-json.js", // Use the local script
},
},
};
function App() {
const [formDefinition, setFormDefinition] = useState();
return (
<>
<FormBuilder
initialForm={formDefinition || { type: "form", display: "form", components: [] }}
options={options}
onChange={(schema) => {
try {
if (!schema || !schema.components || schema.components.length === 0) return;
setFormDefinition(schema);
} catch (error) {
console.error("Error updating form definition:", error);
}
}}
/>
</>
);
}
Most likely, this should be documented in the list of props
@llemire-exp I dont think it has to do with that. I have reproduced it with your suggested changes as well.
https://codesandbox.io/p/sandbox/s2nvtw
@ZenMasterJacob20011 Ive attached reproduced code, I have not used it without react.
Had one of the devs take a look and they had this to say:
The issue in project configuration itself. The initialForm property of the form builder is set to formDefinition which should reflect the current form definition. Each time we add a component, formDefinition gets updated, this means initialForm property gets updated as well which is not true, so FormBuilder tries to update itself because it thinks that the initialForm got changed. This leads to infinite loop and causes builder to stack when we are trying to save component. The solution is to create a separate object form initial form whick won’t be changed.
Here is an example of it formio_bug_6084 (forked)
Thankyou, this worked.