Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to only show validation errors if a field is touched or modified?

I'm using a form with JSON Form and I'm having trouble performing validations. The problem is that the input fields turn red even before the user types anything. I want the error that the field is mandatory to appear only if the user clicks on the field, leaves it blank and clicks away again, or if the user tries to submit the form. Then it shows in red which fields are mandatory.

Can you tell me how to do this treatment correctly? Thank you

The form already has errors appearing when the page is first loaded:

enter image description here

'use client';

import { useState, useEffect } from 'react';
import { JsonForms } from '@jsonforms/react';
import { materialRenderers } from '@jsonforms/material-renderers';
import { materialCells } from '@jsonforms/material-renderers';
import axios from 'axios';
import { Button } from '@mui/material';

const schema = {
  type: 'object',
  properties: {
    first_name: { type: 'string', title: 'First Name' },
    last_name: { type: 'string', title: 'Last Name' },
    email: { type: 'string', title: 'Email' },
    linkedin_url: { type: 'string', title: 'LinkedIn URL' },
  },
  required: ['first_name', 'last_name', 'email', 'linkedin_url'],
};

const uischema = {
  type: 'VerticalLayout',
  elements: [
    { type: 'Control', scope: '#/properties/first_name' },
    { type: 'Control', scope: '#/properties/last_name' },
    { type: 'Control', scope: '#/properties/email' },
    { type: 'Control', scope: '#/properties/linkedin_url' },
  ],
};

export default function Home() {
  const [submitted, setSubmitted] = useState(false);
  const [formData, setFormData] = useState({});
  const [isClient, setIsClient] = useState(false);

  useEffect(() => {
    setIsClient(true);
  }, []);

  const validateForm = () => {
    const errors = {};
    const requiredFields = ['first_name', 'last_name', 'email', 'linkedin_url'];

    requiredFields.forEach((field) => {
      if (!formData[field]) {
        errors[field] = 'This field is required';
      }
    });

    return Object.keys(errors).length === 0;
  };

  const onSubmit = async () => {
    if (validateForm()) {
      try {
        await axios.post('/api/leads', formData);
        setSubmitted(true);
      } catch (error) {
        console.error('Submission failed', error);
      }
    }
  };

  if (submitted) {
    return <p>Thank you for submitting your information!</p>;
  }

  return (
    <div
      style={{
        maxWidth: '500px',
        margin: 'auto',
        padding: '20px',
        border: '1px solid #ccc',
        borderRadius: '8px',
        backgroundColor: '#f9f9f9',
      }}
    >
      {isClient && (
        <JsonForms
          schema={schema}
          uischema={uischema}
          data={formData}
          renderers={materialRenderers}
          cells={materialCells}
          onChange={({ data }) => setFormData(data)}
        />
      )}
      <Button
        onClick={onSubmit}
        variant="contained"
        color="primary"
        style={{ marginTop: '10px' }}
      >
        Submit
      </Button>
    </div>
  );
}
like image 558
vbernal Avatar asked Nov 21 '25 21:11

vbernal


1 Answers

Good question as I could not find sources online too, but I managed to bypass this issue with the below method.

In simplicity, the major idea is to manipulate the validationMode with custom flags; other parts are not as important as they are and can be replaced.

// ...other imports and codes
export default function Home() {
  // ...other useState()

  const [isFirstTimeFormInitiated, setIsFirstTimeFormInitiated] =
    useState(false);
  const [isAccessed, setIsAccessed] = useState(false);

  // ...other useEffect()

  const validateForm = () => {
    // remain unchanged
  };

  const onSubmit = async () => {
    !isAccessed && setIsAccessed(true);
    if (validateForm()) {
      try {
        await axios.post("/api/leads", formData);
        setSubmitted(true);
      } catch (error) {
        console.error("Submission failed", error);
      }
    }
  };

  if (submitted) {
    return <p>Thank you for submitting your information!</p>;
  }

  return (
    <div
      style={{
        // ...other inline styling
      }}
    >
      {isClient && (
        <JsonForms
          schema={schema}
          uischema={uischema}
          data={formData}
          renderers={materialRenderers}
          cells={materialCells}
          onChange={(e) => {
            setFormData(e.data);
            if (!isFirstTimeFormInitiated) {
              // REMARK: A rough way to prevent first time render
              // triggering the setIsAccessed() too early.
              setIsFirstTimeFormInitiated(true);
              return;
            }
            !isAccessed && setIsAccessed(true);
          }}
          // REMARK: Manipulate this with custom flags to hide the errors.
          validationMode={isAccessed ? "ValidateAndShow" : "ValidateAndHide"}
        />
      )}
      <Button
        onClick={onSubmit}
        variant="contained"
        color="primary"
        style={{ marginTop: "10px" }}
      >
        Submit
      </Button>
    </div>
  );
}

You can check the full working example here:

Edit cranky-orla

like image 114
hokwanhung Avatar answered Nov 24 '25 14:11

hokwanhung



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!