Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to validate each form step in material ui stepper?

Typical material-ui stepper below.

  export default function HorizontalLinearStepper() {
    const classes = useStyles();
    const [activeStep, setActiveStep] = React.useState(0);
    const [skipped, setSkipped] = React.useState(new Set());
    const steps = getSteps();

    const isStepOptional = step => {
        return step === 1;
    };

    const isStepSkipped = step => {
        return skipped.has(step);
    };

    const handleNext = () => {
    let newSkipped = skipped;
    if (isStepSkipped(activeStep)) {
        newSkipped = new Set(newSkipped.values());
        newSkipped.delete(activeStep);
    }

    setActiveStep(prevActiveStep => prevActiveStep + 1);
    setSkipped(newSkipped);
};

const handleBack = () => {
    setActiveStep(prevActiveStep => prevActiveStep - 1);
};

const handleSkip = () => {
    if (!isStepOptional(activeStep)) {
        // You probably want to guard against something like this,
        // it should never occur unless someone's actively trying to break something.
        throw new Error("You can't skip a step that isn't optional.");
    }

    setActiveStep(prevActiveStep => prevActiveStep + 1);
    setSkipped(prevSkipped => {
        const newSkipped = new Set(prevSkipped.values());
        newSkipped.add(activeStep);
        return newSkipped;
    });
};

const handleReset = () => {
    setActiveStep(0);
};

return (
    <div className={classes.root}>
        <Stepper activeStep={activeStep}>
            {steps.map((label, index) => {
                const stepProps = {};
                const labelProps = {};
                if (isStepOptional(index)) {
                    labelProps.optional = <Typography variant="caption">Optional</Typography>;
                }
                if (isStepSkipped(index)) {
                    stepProps.completed = false;
                }
                return (
                    <Step key={label} {...stepProps}>
                        <StepLabel {...labelProps}>{label}</StepLabel>
                    </Step>
                );
            })}
        </Stepper>
        <div>
            {activeStep === steps.length ? (
                <div>
                    <Typography className={classes.instructions}>
                        All steps completed - you&apos;re finished
                    </Typography>
                    <Button onClick={handleReset} className={classes.button}>
                        Reset
                    </Button>
                </div>
            ) : (
                <div>
                    <div className={classes.instructions}>{getStepContent(activeStep)}</div>
                    <div>
                        <Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
                            Back
                        </Button>
                        {isStepOptional(activeStep) && (
                            <Button
                                variant="contained"
                                color="primary"
                                onClick={handleSkip}
                                className={classes.button}
                            >
                                Skip
                            </Button>
                        )}

                        <Button
                            variant="contained"
                            color="primary"
                            onClick={handleNext}
                            type="submit"
                            className={classes.button}
                        >
                            {activeStep === steps.length - 1 ? 'Finish' : 'Next'}
                        </Button>
                    </div>
                </div>
            )}
        </div>
    </div>
    );
}

Here is function i create to choice step

function getStepContent(step) {
    switch (step) {
        case 0:
            return <Step1/>;
        case 1:
            return <Step2/>;
        case 2:
            return 'This is the bit I really care about!';
        default:
            return 'Unknown step';
    }
}

Step1 and Step2 are components that has 2 forms inside build with react-final-form

import React, { Component } from 'react';
import { Form } from 'react-final-form';
import initialValuesCreator from './creationMethods/initialValuesCreator';
import { validationCreator } from './creationMethods/validationSchemaCreator';

class CustomValidationForm extends Component {

    render() {
        const {
            config ,children, submit = () => {}
        } = this.props;

        return (
            <Form
                onSubmit={(event) => {
                    submit(event);
                }}
                initialValues={initialValuesCreator(config)}
                validate={values => validationCreator(values, config)}
                render={({handleSubmit}) => (
                    <form noValidate autoComplete={'off'} onSubmit={handleSubmit}>
                        {children}
                    </form>
                )}
            />
        )
    }
}

And here comes the question. Material ui stepper has handleNext function. Its my submit for each step. Each step will be some kind of form with validation. When user is on step1 and he press submit i want to show him input errors (something is required etc) and prevent jumping to next step. Step1 can have multiple small forms so all the form should be validated when handleNext is pressed.

like image 829
wantToBeAProgrammer Avatar asked Feb 02 '20 18:02

wantToBeAProgrammer


People also ask

Does material UI have form validation?

To add form validation with React and Material UI, we can set the error prop of the TextField to show the error state when there's an error. We can set the helperText prop to show the error message. We add a TextField with the value prop set to text to show the inputted text.

Is material UI good for performance?

Material UI provides developers with material-based UI components. They help save some time on design and app development. But since Material UI is mostly a set of UI components, it doesn't offer such a great boost to the development speed as Bootstrap does.

How do I run a material UI code?

Open your terminal, and ensure that you are inside your application's master folder. Type npm install @material-ui/core in the terminal and click enter. Run the command below to use Material-UI icons in our project. We can confirm that the above dependencies have been installed by checking in the package.


1 Answers

I was dealing with it as well What I finally did to resolve it:

  1. Wrapping the pages with a form (instead of the div)
  2. Setting a reference to the form using the 'useRef' react hook
  3. Check whether or not the form is valid on the 'handleNext' method and return if it's not. The access to the form object is achieved by the useRef hook, and the validity check made by the function 'myForm.current.checkValidity()'

Please give it a shot and let me know if it works for you:

export default function HorizontalLinearStepper() {
    const classes = useStyles();
    const [activeStep, setActiveStep] = React.useState(0);
    const [skipped, setSkipped] = React.useState(new Set());
    const steps = getSteps();

    const myForm = React.useRef(null);

    const isStepOptional = step => {
        return step === 1;
    };

    const isStepSkipped = step => {
        return skipped.has(step);
    };

    const handleNext = () => {
        if (!myForm.current.checkValidity()) {
           return;
        }
        let newSkipped = skipped;
        if (isStepSkipped(activeStep)) {
            newSkipped = new Set(newSkipped.values());
            newSkipped.delete(activeStep);
        }
    
        setActiveStep(prevActiveStep => prevActiveStep + 1);
        setSkipped(newSkipped);
    };
    
    const handleBack = () => {
        setActiveStep(prevActiveStep => prevActiveStep - 1);
    };
    
    const handleSkip = () => {
        if (!isStepOptional(activeStep)) {
            // You probably want to guard against something like this,
            // it should never occur unless someone's actively trying to break something.
            throw new Error("You can't skip a step that isn't optional.");
         }
     
        setActiveStep(prevActiveStep => prevActiveStep + 1);
        setSkipped(prevSkipped => {
            const newSkipped = new Set(prevSkipped.values());
            newSkipped.add(activeStep);
            return newSkipped;
        });
    };

    const handleReset = () => {
        setActiveStep(0);
    };

return (
    <div className={classes.root}>
        <Stepper activeStep={activeStep}>
            {steps.map((label, index) => {
                const stepProps = {};
                const labelProps = {};
                if (isStepOptional(index)) {
                    labelProps.optional = <Typography variant="caption">Optional</Typography>;
                }
                if (isStepSkipped(index)) {
                    stepProps.completed = false;
                }
                return (
                    <Step key={label} {...stepProps}>
                        <StepLabel {...labelProps}>{label}</StepLabel>
                    </Step>
                );
            })}
        </Stepper>
        <form action="/" method="POST" ref={myForm}>
            {activeStep === steps.length ? (
                <div>
                    <Typography className={classes.instructions}>
                        All steps completed - you&apos;re finished
                    </Typography>
                    <Button onClick={handleReset} className={classes.button}>
                        Reset
                    </Button>
                </div>
            ) : (
                <div>
                    <div className={classes.instructions}>{getStepContent(activeStep)}</div>
                    <div>
                        <Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
                            Back
                        </Button>
                        {isStepOptional(activeStep) && (
                            <Button
                                variant="contained"
                                color="primary"
                                onClick={handleSkip}
                                className={classes.button}
                            >
                                Skip
                            </Button>
                        )}

                        <Button
                            variant="contained"
                            color="primary"
                            onClick={handleNext}
                            type="submit"
                            className={classes.button}
                        >
                            {activeStep === steps.length - 1 ? 'Finish' : 'Next'}
                        </Button>
                    </div>
                </form>
            )}
        </div>
    </div>
    );
}
like image 179
y-me Avatar answered Oct 20 '22 22:10

y-me