Is it possible to link fields in a way where the input event on one can trigger the value to change in another field? It looks likes it's possible using Collection hooks, but it wouldn't be reactive and the target field would only update on save.
My use case is I want to populate a slug
field with a slugified version of what is entered into the title
field for a Page
, where the slug
field updates as the user enters the value in the title
field. Would this be something that is achieved with custom components?
As an aside, is it possible to set attributes on a field's <input>
element (e.g. set the disabled
attribute)? I've seen that there's an admin.disabled
property that can be passed to fields, but this removes the field entirely from the admin interface.
Thanks!
Hey @slavanossar — this is absolutely possible. You're right in that you can use collection hooks to modify field values on the server-side, on save, but for your case, you're likely to benefit more from client-side component-based implementation.
Yep - custom components are the way to go. Something like this:
import React, { useEffect } from 'react';
// You can import the text input on its own if you have payload@0.13.8-beta.0
import { TextInput, useWatchForm, useField } from 'payload/components/forms';
const CustomTextInput = ({ name, path, label }) => {
// This will be documented more fully once we have the API solidified
// But for now, you can take a look at Payload's source code or use TS to see what
// is available here.
// `useWatchForm` is going to update every time **any** field changes so you'll always
// have an up-to-date copy of your title field's data
const { getDataByPath } = useWatchForm();
const {
showError,
value,
setValue,
} = useField({
path,
enableDebouncedValue: true,
});
// Grab your title's field value
const titleValue = getDataByPath('title');
// When your title field changes, set the value of your field
useEffect(() => {
setValue(titleValue);
}, [setValue, titleValue]);
return (
<TextInput
path={path}
name={name}
value={value as string || ''}
label={label}
showError={showError}
/>
);
};
export default CustomTextInput;
How's that?
Note, Payload's built-in TextInput
is only released as beta for now. We will be improving and solidifying the API as to how to re-use built-in components like this but if you are comfortable with doing so, go for it. Otherwise, you can use a regular old <input />
if you want.
Thanks for that, looks great!
Only thing is I'm having an issue with getDataByPath('title')
returning undefined
. I naturally tried out some nested fields too (e.g. programFields.pageHeader.heading
), and it doesn't return values for them either. Logging the fields
within getDataByPath
shows the fields are there.
I took a peek at the source and it seems like the condition on line 10 would never match any field (top-level or nested), since it's appending a .
to the original path
that is passed to the function:
payload/src/admin/components/forms/Form/getDataByPath.ts
Lines 6 to 18 in 35bf092
Unless I'm missing something, I think it should just be:
if (key === path) {
Also thanks for providing the code snippet, I'm coming from Vue/Nuxt and still trying to wrap my head around React syntax and conventions, so it's super helpful to have 👍
Very good catch!
We use this function internally to get all descendant field values by path, so for example, if you have an Array and want to get all of the array's child field values so it ends up as an array of objects.
So we need to keep the existing if
, but we can add key === path
like you mentioned. Everything works with both cases present.
I just deployed a beta version payload@0.13.11-beta.0
with this fix. Give it a shot!
Star
Discord
online
Get help straight from the Payload team with an Enterprise License.