Search code examples
javascriptreactjsvalidationfluentui-react

Validate other fluentui textfield based on the first one


I have a question regarding validation with react and fluentui. I have following situation: 2 textfields and if one is filled in then the other should be filled in as well. Just validation is simple, I used onGetErrorMessage, which sets error message if on input the value does not meet the requirements. Now how can I set value on the other textfields, is it possible to trigger onGetErrorMessage of the textfield number two? For example if I have a reference or maybe some other way?

Const validate = (value: string, order: string) => string {
// if(order === 'one') => show error message that the second has to be filled in as well in case it is empty
// if(order === 'two') => show error message that the first has to be filled in in case it is empty
           return value.length > 0 ? ‘’ : ‘Required’;
}

<TextField label = “Field 1” value = {field1} .... onGetErrorMessage={(value:string) => validate(value, 'one')} />
<TextField label = “Field 2” value = {field2} .... onGetErrorMessage={(value:string) => validate(value, 'two')} />

This would love to have as well like some generic solution... any idea how to achieve it?

Thanks in advance.


Solution

  • I think you can't trigger onGetErrorMessage by reference (I didn't find the way, share if you find out).

    My idea is to create generic FormComponent with following structure:

    type Field = { options?: { label?: string }; validation?: string };
    type Form<T> = { [P in keyof T]: Field };
    
    type Props<T> = {
      fields: Form<T>;
      value: T & { [key: string]: string };
      changeFormValue: (name: keyof Form<T>, value?: string) => void;
    };
    
    const FormComponent = <T extends unknown>({
      fields,
      changeFormValue,
      value
    }: Props<T>) => {
      return (
        <>
          {(Object.keys(fields) as Array<keyof Form<T>>).map(field => {
            const { validation, options } = fields[field];
            return (
              <TextField
                label={options?.label}
                value={value[field]}
                errorMessage={validation} // Control validation
                onChange={(_: any, value?: string) => changeFormValue(field, value)}
              />
            );
          })}
        </>
      );
    };
    

    Implementation:

    import { Field, FormComponent } from 'common/form'
    
    type Form = {
      firstName: Field
      lastName: Field
    }
    
    type FormValue = {
      firstName: string
      lastName: string
    }
    
    const HomeComponent = () => {
      const [form, setForm] = React.useState<FormValue>({
        firstName: "",
        lastName: ""
      });
    
      // Place where you can control validation
      const viewForm = ({ firstName, lastName }: FormValue): Form => ({
        firstName: {
          validation: firstName.length === 0 ? "Field is required" : undefined,
          options: {
            label: "First name"
          }
        },
        lastName: {
          validation:
            lastName.length === 0 && firstName.length > 0
              ? "Field is required"
              : undefined,
          options: {
            label: "Last name"
          }
        }
      });
    
      return (
        <>
          {FormComponent<FormValue>({
            fields: viewForm(form),
            changeFormValue: (field, value) => setForm({ ...form, [field]: value }),
            value: form
          })}
        </>
      );
    };
    

    If you need stronger validation or you have complex form, it's better to use some library. (Formik, TComb)

    Codepen working example.