I have a use case where I'd like to be able to configure form fields in the admin on the fly depeniding on a relationship.
Requirements:
- relationships require no pre-set fields in the collection config
- relationships have a config field that accepts fieldsSchema
- When relationship is selected collection will populate all fields under path
config[relationshipField]
So far i have someting basic working by hijacking the
<RenderFields>
component inside a UI Field, but there's some holes in my implementation but it doesn't really work.
Is there a prescribed way to dynamically add fields to a form if they aren't predefined in the collection config?
Sample provider
Sample connection
The fields show up but don't sync with data from the form
const [fields, dispatchFields] = useAllFormFields();
const fieldBuilder = (config) => {
const renderFields = [];
for (let field of config) {
switch (field.type)
{
case 'text':
renderFields.push(
<TextField
admin={{}}
key={field.name}
name={field.name}
path={`config.${field.name}`}
label={field.name}
onChange={e => {}}
value={fields.config.value[field.name]}
/>
);
break;
case 'textarea':
renderFields.push(
<TextareaField
admin={{}}
key={field.name}
name={field.name}
path={`config.${field.name}`}
label={field.name}
onChange={e => {}}
value={fields.config.value[field.name]}
/>
);
break;
}
}
return renderFields;
}
That's how I have it rendering the fields so far
bump
I was having too many issues with this implementation. I ended up switching to using an array of key/val pairs for my config and I created a ui field to ensure the correct rows are added to the array when selecting a provider
import { useField, useFormFields, useForm } from 'payload/components/forms';
import React, { useState, useEffect } from 'react'
const EnsureConnectionConfigField: React.FC = () => {
const { value: providerId } = useField({ path: 'provider' });
const { addFieldRow, getData } = useForm();
useEffect(() => {
(async () => {
if (providerId) {
const provider = await fetch(`/api/connection-providers/${providerId}`).then(r => r.json());
const { config } = getData();
let configRows = [];
if (Array.isArray(config)) {
configRows = [...config];
}
for (let defaultConfig of provider.config) {
if (!configRows.some(c => c.key == defaultConfig.key)) {
addFieldRow({
path: 'config',
data: {
key: defaultConfig.key,
val: defaultConfig.defaultValue || ''
}
})
}
}
}
})();
}, [providerId]);
};
export default EnsureConnectionConfigField
this works well enough for now.
Star
Discord
online
Get help straight from the Payload team with an Enterprise License.