Skip to main content

13.11 Adding Validation Feedback

Input.js

Input.js
import React from "react";
import classes from "./Input.module.css";

const input = (props) => {
let inputElement = null;
const inputClasses = [classes.InputElement];

if (props.invalid && props.shouldValidate) {
inputClasses.push(classes.Invalid);
}

switch (props.elementType) {
case "input":
inputElement = (
<input
className={inputClasses.join(" ")}
{...props.elementConfig}
value={props.value}
onChange={props.changed}
/>
);
break;
case "textarea":
inputElement = (
<textarea
className={inputClasses.join(" ")}
{...props.elementConfig}
value={props.value}
onChange={props.changed}
/>
);
break;
case "select":
inputElement = (
<select
className={inputClasses.join(" ")}
value={props.value}
onChange={props.changed}
>
{props.elementConfig.options.map((option) => (
<option key={option.value} value={option.value}>
{option.displayValue}
</option>
))}
</select>
);
break;
default:
inputElement = (
<input
className={inputClasses.join(" ")}
{...props.elementConfig}
value={props.value}
onChange={props.changed}
/>
);
}
return (
<div className={classes.Input}>
<label className={classes.Label}>{props.label}</label>
{inputElement}
</div>
);
};

export default input;

Input.module.css

Input.module.css
.Input {
width: 100%;
padding: 10px;
box-sizing: border-box;
}

.Label {
font-weight: bold;
display: block;
margin-bottom: 8px;
}

.InputElement {
outline: none;
border: 1px solid #ccc;
background-color: #fff;
font: inherit;
padding: 6px 10px;
display: block;
width: 100%;
box-sizing: border-box;
}

.InputElement:focus {
outline: none;
background-color: #ccc;
}

.Invalid {
border: 1px solid red;
background-color: #fda49a;
}

ContactData.js

ContactData.js
import React, { Component } from "react";
import axios from "../../../axios-orders";
import Button from "../../../components/UI/Button/Button";
import Input from "../../../components/UI/Input/Input";
import Spinner from "../../../components/UI/Spinner/Spinner";
import classes from "./ContactData.module.css";

class ContactData extends Component {
state = {
orderForm: {
name: {
elementType: "input",
elementConfig: {
type: "text",
placeholder: "Your Name",
},
value: "",
validation: {
required: true,
},
valid: false,
},
street: {
elementType: "input",
elementConfig: {
type: "text",
placeholder: "Street",
},
value: "",
validation: {
required: true,
},
valid: false,
},
zipCode: {
elementType: "input",
elementConfig: {
type: "text",
placeholder: "ZIP Code",
},
value: "",
validation: {
required: true,
minLength: 5,
maxLength: 5,
},
valid: false,
},
country: {
elementType: "input",
elementConfig: {
type: "text",
placeholder: "Country",
},
value: "",
validation: {
required: true,
},
valid: false,
},
email: {
elementType: "input",
elementConfig: {
type: "email",
placeholder: "Your e-mail",
},
value: "",
validation: {
required: true,
},
valid: false,
},
deliveryMethod: {
elementType: "select",
elementConfig: {
options: [
{ value: "fastest", displayValue: "Fastest" },
{ value: "cheapest", displayValue: "Cheapest" },
],
},
value: "",
},
},
loading: false,
};

orderHandler = (event) => {
event.preventDefault();
this.setState({ loading: true });
const formData = {};
for (let formElementIdentifier in this.state.orderForm) {
formData[formElementIdentifier] = this.state.orderForm[
formElementIdentifier
].value;
}
const order = {
ingredients: this.props.ingredients,
price: this.props.price,
orderData: formData,
};
axios
.post("/orders.json", order)
.then((response) => {
this.setState({ loading: false });
this.props.history.push("/");
})
.catch((error) => {
this.setState({ loading: false });
});
};

checkValidity(value, rules) {
let isValid = true;
if (rules.required) {
isValid = value.trim() !== "" && isValid;
}

if (rules.minLength) {
isValid = value.length >= rules.minLength && isValid;
}

if (rules.maxLength) {
isValid = value.length <= rules.maxLength && isValid;
}

return isValid;
}

inputChangedHandler = (event, inputIdentifier) => {
const updatedOrderForm = {
...this.state.orderForm,
};
const updatedFormElement = {
...updatedOrderForm[inputIdentifier],
};
updatedFormElement.value = event.target.value;
updatedFormElement.valid = this.checkValidity(
updatedFormElement.value,
updatedFormElement.validation
);
updatedOrderForm[inputIdentifier] = updatedFormElement;
console.log(updatedFormElement);
this.setState({ orderForm: updatedOrderForm });
};
render() {
const formElementsArray = [];
for (let key in this.state.orderForm) {
formElementsArray.push({
id: key,
config: this.state.orderForm[key],
});
}
let form = (
<form onSubmit={this.orderHandler}>
{formElementsArray.map((formElement) => (
<Input
key={formElement.id}
elementType={formElement.config.elementType}
elementConfig={formElement.config.elementConfig}
value={formElement.config.value}
invalid={!formElement.config.valid}
shouldValidate={formElement.config.validation}
changed={(event) => this.inputChangedHandler(event, formElement.id)}
/>
))}
<Button btnType="Success">ORDER</Button>
</form>
);
if (this.state.loading) {
form = <Spinner />;
}
return (
<div className={classes.ContactData}>
<h4>Enter your Contact Data</h4>
{form}
</div>
);
}
}
export default ContactData;