Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

Linking field values

default discord avatar
slavanossar2 years ago
1 1

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!

  • Selected Answer
    discord user avatar
    jmikrut
    2 years ago

    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.

    2 replies
  • default discord avatar
    slavanossar2 years ago

    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:

    const pathPrefixToRemove = path.substring(0, path.lastIndexOf('.') + 1);
    const name = path.split('.').pop();
    const data = Object.keys(fields).reduce((matchedData, key) => {
    if (key.indexOf(`${path}.`) === 0) {
    return {
    ...matchedData,
    [key.replace(pathPrefixToRemove, '')]: fields[key],
    };
    }
    return matchedData;
    }, {});

    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 👍

  • discord user avatar
    jmikrut
    2 years ago

    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 on GitHub

Star

Chat on Discord

Discord

online

Can't find what you're looking for?

Get help straight from the Payload team with an Enterprise License.