Related
I am creating a form using react-bootstrap, react-hook-form, and yup for validation.
I have several fields that are not required like url and radio.
I have used a custom regex instead of yup's url() as it is really strict, for example not accepting google.com or www.google.com.
Also, there are radio type fields like offerInternships, offerVisaSponsorships, offerRemoteWorking which I have declared as notRequired in yup's schema.
The problem is when I press submit button, errors are shown indicating that they must be filled, and I only be able to proceed if I filled them with correct values.
I am not really sure what could have gone wrong here.
Edit 1: I have modified the regex to accept empty strings by adding |^$ at the end of re, and the url now can accept empty strings and act as if it's not required (I am not sure if this actually fixes the problem), but the radio type's problem still exists.
import { Container, Row, Col, Button, Form } from "react-bootstrap";
import * as yup from "yup";
import { useForm } from "react-hook-form";
import { yupResolver } from "#hookform/resolvers/yup";
/**
* Company's validation schema.
*/
const re =
/^((ftp|http|https):\/\/)?(www.)?(?!.*(ftp|http|https|www.))[a-zA-Z0-9_-]+(\.[a-zA-Z]+)+((\/)[\w#]+)*(\/\w+\?[a-zA-Z0-9_]+=\w+(&[a-zA-Z0-9_]+=\w+)*)?$/gm;
const formSchema = yup.object().shape({
url: yup.string().matches(re, "Enter a valid URL!").required(),
name: yup.string().required(),
location: yup.object().shape({
country: yup.string().required(),
state: yup.string().required(),
city: yup.string().required(),
address: yup.string().required(),
}),
offerInternships: yup.bool().default(false).notRequired(),
offerRemoteWork: yup.bool().default(false).notRequired(),
offerVisaSponsorship: yup.bool().default(false).notRequired(),
socialMedia: yup.object().shape({
Linkedin: yup.string().matches(re, "Enter a valid URL!").notRequired(),
GitHub: yup.string().matches(re, "Enter a valid URL!").notRequired(),
}),
});
const RequestForm = () => {
const {
register,
handleSubmit,
watch,
formState: { errors },
} = useForm({
resolver: yupResolver(formSchema),
mode: "all",
});
/**
* Handling the submittion of the form
*/
const onSubmit = async (data) => {
try {
console.log(errors);
console.log(data);
} catch (error) {
console.log(error);
}
};
return (
<>
<Container id="RequestForm-UserForm" className="mt-5 outer-border p-5">
<Form onSubmit={handleSubmit(onSubmit)}>
<Row id="company-details-row">
<Col id="company-details-row-input" sm={12} md={9}>
<Form.Group className="mb-2" controlId="formBasicEmail">
<Form.Label>Company's name</Form.Label>
<Form.Control
type="text"
placeholder="e.g. Microsoft"
{...register("name")}
isInvalid={!!errors?.name}
/>
</Form.Group>
<Form.Group className="mb-2" controlId="formBasicEmail">
<Form.Label>Company's website</Form.Label>
<Form.Control
type="text"
placeholder="e.g. https://www.microsoft.com"
{...register("url")}
isInvalid={!!errors.url}
/>
</Form.Group>
<h3 className="mb-0">Location</h3>
<Form.Group className="mb-2" controlId="formBasicEmail">
<Form.Label>Country</Form.Label>
<Form.Control
type="text"
placeholder="e.g. Egypt, United States of America,...etc."
{...register("location.country")}
isInvalid={!!errors.location?.country}
/>
</Form.Group>
<Form.Group className="mb-2" controlId="formBasicEmail">
<Form.Label>State/Governorate</Form.Label>
<Form.Control
type="text"
placeholder="e.g. Cairo, California, Berlin...etc."
{...register("location.state")}
isInvalid={!!errors.location?.state}
/>
</Form.Group>
<Form.Group className="mb-2" controlId="formBasicEmail">
<Form.Label>City</Form.Label>
<Form.Control
type="text"
placeholder="e.g. Maadi, San Francisco,...etc."
{...register("location.city")}
isInvalid={!!errors.location?.city}
/>
</Form.Group>
<Form.Group className="mb-2" controlId="formBasicEmail">
<Form.Label>Address</Form.Label>
<Form.Control
type="text"
placeholder="e.g. 14th Zaki Sayed St."
{...register("location.address")}
isInvalid={!!errors.location?.address}
/>
</Form.Group>
<h3 className="mt-2 mb-0">Social Media</h3>
<Form.Group className="mb-2" controlId="formBasicEmail">
<Form.Label>
<i className="icon ion-social-linkedin"></i> Linkedin
</Form.Label>
<Form.Control
type="text"
placeholder="e.g. https://www.linkedin.com/company/x"
{...register("socialMedia.Linkedin")}
isInvalid={!!errors.socialMedia?.Linkedin}
/>
</Form.Group>
<Form.Group className="mb-2" controlId="formBasicEmail">
<Form.Label>
<i className="icon ion-social-github"></i> GitHub
</Form.Label>
<Form.Control
type="text"
placeholder="e.g. https://www.github.com/company"
{...register("socialMedia.GitHub")}
isInvalid={!!errors.socialMedia?.GitHub}
/>
</Form.Group>
<h3 className="mt-2 mb-0">Culture & Environment</h3>
<div id="offer-Internship" key={"inline-radio-internships"}>
<Form.Label>Does your company offer internships? 🎓</Form.Label>
<br></br>
<Form.Check
inline
label="Yes"
value={"true"}
{...register("offerInternships", { required: false })}
type={"radio"}
isInvalid={!!errors.offerInternships}
/>
<Form.Check
inline
label="No"
value={"false"}
{...register("offerInternships", { required: false })}
type={"radio"}
isInvalid={!!errors.offerInternships}
/>
</div>
<div id="offer-VisaSponsorship" key={"inline-radio-visa"}>
<Form.Label>
Does your company offer visa sponsorship? ✈️
</Form.Label>
<br></br>
<Form.Check
inline
label="Yes"
value={"true"}
{...register("offerVisaSponsorship", { required: false })}
type={"radio"}
isInvalid={!!errors.offerVisaSponsorship}
/>
<Form.Check
{...register("offerVisaSponsorship", { required: false })}
inline
label="No"
value={"false"}
type={"radio"}
isInvalid={!!errors.offerVisaSponsorship}
/>
</div>
<div id="offer-remoteWork" key={"inline-radio-remote"}>
<Form.Label>
Does your company offer remote working? 🗺️
</Form.Label>
<br></br>
<Form.Check
{...register("offerRemoteWork", { required: false })}
inline
label="Yes"
value={"true"}
type={"radio"}
isInvalid={!!errors.offerRemoteWork}
/>
<Form.Check
{...register("offerRemoteWork", { required: false })}
inline
label="No"
value={"false"}
type={"radio"}
isInvalid={!!errors.offerRemoteWork}
/>
</div>
</Col>
</Row>
<Row id="company-submit text-center">
<Col className={"text-center"} sm={12}>
<h2 className="fs-1 main-color-1-dark">
We have reached to the end of the form! 🎆
</h2>
<Button variant="success" type="submit" className="rubik-font">
Submit form
</Button>
</Col>
</Row>
</Form>
</Container>
</>
);
};
export default RequestForm;
I have a Home component that fetches data from the database and show it.
import React, {Component} from 'react';
import { Jumbotron,Modal,Button,ModalHeader,ModalBody,
Form,FormGroup,Input,Label} from 'reactstrap';
import { Card, CardTitle, CardText, Row, Col } from 'reactstrap';
import { CardHeader, CardFooter, CardBody } from 'reactstrap';
import axios from 'axios';
import BlogAddForm from './BlogAddForm';
import NavbarComponent from './NavbarComponent';
class HomeComponent extends Component{
constructor(){
super();
this.state={
isSignupModalOpen:false,
isLoginModalOpen:false,
isblogOpen:false,
blogs:[]
}
this.toggleSignupModal=this.toggleSignupModal.bind(this);
this.toggleLoginModal=this.toggleLoginModal.bind(this);
this.toggleblogModal=this.toggleblogModal.bind(this);
}
componentDidMount() {
console.log("component did mount");
axios.get('http://localhost:3000/')
.then(response => {
if (response.data.length > 0) {
this.setState({
blogs: response.data
});
}
})
.catch((error) => {
console.log(error);
})
}
toggleSignupModal(){
this.setState({
isSignupModalOpen:!this.state.isSignupModalOpen
})
}
toggleLoginModal(){
this.setState({
isLoginModalOpen:!this.state.isLoginModalOpen
})
}
toggleblogModal(){
this.setState({
isblogOpen:!this.state.isblogOpen
})
}
render(){
console.log("inside render ");
var b=this.state.blogs.map((blog)=>{
console.log(blog);
var taggu=blog.tags.map((tag)=>{
return(
<span>#{tag}</span>
)
});
var con=blog.content.slice(0,100);
return(
<Col className="my-1" lg="4" sm="6" >
<Card key={blog._id}>
<CardHeader tag="h3">{blog.topic}</CardHeader>
<CardBody>
<CardTitle tag="h5">By {blog.writer.username}</CardTitle>
<CardText>{con}... </CardText>
<Button>Learn More</Button>
</CardBody>
<CardFooter>{taggu}</CardFooter>
</Card>
</Col>
)
});
return (
<div className="App">
<NavbarComponent />
<Jumbotron>
<h1 className="display-5">WELCOME TO BLOGERA</h1>
<p className="lead">This is a simple blog site where you can put your thoughts into words and
interact with people.</p>
<p className="my-2">
<Button color="success" onClick={this.toggleblogModal}>Add Blog</Button>
</p>
</Jumbotron>
{b}
<Modal isOpen={this.state.isLoginModalOpen} toggle={this.toggleLoginModal}>
<ModalHeader toggle={this.toggleLoginModal}>Login</ModalHeader>
<ModalBody>
<Form onSubmit={this.handleLogin}>
<FormGroup>
<Label htmlFor="username">Username</Label>
<Input type="text" id="username" name="username" innerRef=
{(input)=>this.username=input}/>
</FormGroup>
<FormGroup>
<Label htmlFor="password">Password</Label>
<Input type="password" id="password" name="password" innerRef=
{(input)=>this.password=input} />
</FormGroup>
<FormGroup check>
<Label check>
<Input type="checkbox" name="remember" innerRef=
{(input)=>this.remember=input}/>
Remember me
</Label>
</FormGroup>
<Button type="submit" value="submit" color="primary">Login</Button>
</Form>
</ModalBody>
</Modal>
<Modal isOpen={this.state.isSignupModalOpen} toggle={this.toggleSignupModal}>
<ModalHeader toggle={this.toggleSignupModal}>Signup</ModalHeader>
<ModalBody>
<Form onSubmit={this.handleLogin}>
<FormGroup>
<Label htmlFor="username">Username</Label>
<Input type="text" id="username" name="username" innerRef=
{(input)=>this.username=input}/>
</FormGroup>
<FormGroup>
<Label htmlFor="password">Password</Label>
<Input type="password" id="password" name="password" innerRef=
{(input)=>this.password=input} />
</FormGroup>
<FormGroup check>
<Label check>
<Input type="checkbox" name="remember" innerRef=
{(input)=>this.remember=input}/>
Remember me
</Label>
</FormGroup>
<Button type="submit" value="submit" color="primary">Signup</Button>
</Form>
</ModalBody>
</Modal>
<Modal isOpen={this.state.isblogOpen} toggle={this.toggleblogModal}>
<ModalHeader toggle={this.toggleblogModal}>Write Blog</ModalHeader>
<ModalBody>
<BlogAddForm toggleblogModal={this.toggleblogModal} />
</ModalBody>
</Modal>
<Row>
<Col className="my-1" lg="4" sm="6" >
<Card>
<CardHeader tag="h3">Header</CardHeader>
<CardBody>
<CardTitle tag="h5">Special Title Treatment</CardTitle>
<CardText>With supporting text below as a natural lead-in to additional content.</CardText>
<Button>Learn More</Button>
</CardBody>
<CardFooter>Footer</CardFooter>
</Card>
</Col>
<Col className="my-1" lg="4" sm="6" >
<Card>
<CardHeader tag="h3">Featured</CardHeader>
<CardBody>
<CardTitle tag="h5">Special Title Treatment</CardTitle>
<CardText>With supporting text below as a natural lead-in to additional content.</CardText>
<Button>Learn More</Button>
</CardBody>
<CardFooter className="text-muted">Footer</CardFooter>
</Card>
</Col>
<Col className="my-1" lg="4" sm="6" >
<Card>
<CardHeader tag="h3">Featured</CardHeader>
<CardBody>
<CardTitle tag="h5">Special Title Treatment</CardTitle>
<CardText>With supporting text below as a natural lead-in to additional content.</CardText>
<Button>Learn More</Button>
</CardBody>
<CardFooter className="text-muted">Footer</CardFooter>
</Card>
</Col>
</Row>
</div>
);
}
}
export default HomeComponent;
BLOGADDFORM IS a component which takes data and send post request at backend. I want that as database gets updated my homecomponent should re render with the updated data. I tried redirecting to my home component but that does not solve the purpose.
this is my blog add form component
import React, {Component} from 'react';
import { Button,Form,FormGroup,Input,Label} from 'reactstrap';
import { Redirect } from 'react-router-dom';
import axios from 'axios';
class BlogAddForm extends Component{
constructor(){
super();
this.state = {
input: {},
errors: {}
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
let input = this.state.input;
input[event.target.name] = event.target.value;
this.setState({
input:input
});
}
handleSubmit(event) {
event.preventDefault();
if(this.validate()){
console.log(this.state);
axios.post("http://localhost:3000/nb", {
params:{
data:this.state.input
}
}).then((res)=>{
let input = {};
input["topic"] = "";
input["content"] = "";
input["tag"] = "";
this.setState({input:input});
alert('Blog Added');
this.props.toggleblogModal();
<Redirect to='/' />
}).catch((err)=>{
console.log(err);
})
}
}
validate(){
let input = this.state.input;
let errors = {};
let isValid = true;
if (!input["topic"]) {
isValid = false;
errors["topic"] = "Please enter the topic of blog.";
}
if (!input["content"]) {
isValid = false;
errors["content"] = "Please write some content.";
}
if (!input["tag"]) {
isValid = false;
errors["tag"] = "Please enter tags related to this blog.";
}
this.setState({
errors: errors
});
return isValid;
}
render(){
return(
<Form onSubmit={this.handleSubmit}>
<FormGroup>
<Label htmlFor="topic">Topic</Label>
<Input type="text" id="topic" name="topic"
value={this.state.input.topic}
onChange={this.handleChange}
/>
<div className="text-danger">{this.state.errors.topic}</div>
</FormGroup>
<FormGroup>
<Label htmlFor="content">Content</Label>
<Input type="textarea" id="content" name="content" rows={8}
value={this.state.input.content}
onChange={this.handleChange}
/>
<div className="text-danger">{this.state.errors.content}</div>
</FormGroup>
<FormGroup>
<Label htmlFor="tag">Tags</Label>
<Input type="text" id="tag" name="tag"
value={this.state.input.tag}
onChange={this.handleChange}
/>
<div className="text-danger">{this.state.errors.tag}</div>
</FormGroup>
<Button type="submit" value="submit" color="primary">Submit</Button>
</Form>
)
}
}
export default BlogAddForm;
You'll need to use componentDidUpdate() when reentering the Home component view.
Extract your network request into a different function and call it from both componentDidMount() and componentDidUpdate().
I'm trying to set form validation in React-bootstrap.
I've just written code in accordance with React-bootstrap document.
https://react-bootstrap.netlify.app/components/forms/#forms-validation
I confirmed validation is activated and data is changed when I push submit button.
But only setTimeout method is not working because of adding React-bootstrap validation code.
I couldn't find where I missed and should fix.
Could you tell me the differences between my code and document.
Also, I add comments to my code to make clearly.
PS: I debugged my code. I found that that code was skipped.
I am investigating causes now.
.then(response => {
if (response.data != null) {
alert('編集完了!')
window.setTimeout(() => {
history.push("/")
}, 1000)
}
})
Before adding code
import axios from 'axios'
import { useEffect, useState } from 'react'
import { Card, Form, Button, Col } from 'react-bootstrap';
import React, { Component } from 'react'
import { BrowserRouter, Route, Link, useHistory } from 'react-router-dom'
import Box from '#material-ui/core/Box';
import { Copyright } from './Copyright';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faList, faHome, faClipboardCheck } from '#fortawesome/free-solid-svg-icons'
export const EditData = (props) => {
const history = useHistory()
const id = props.listNo;
const [dataOne, setDataOne] = useState({});
useEffect(() => {
axios.get("http://localhost:8080/action/edit/" + id).then((response) =>
setDataOne(response.data)
)
}, [])
const handleChange = event => {
setDataOne({ ...dataOne, [event.target.name]: event.target.value });
};
const handleSubmit = event => {
event.preventDefault();
const list = {
listNo: dataOne.listNo,
softwareName: dataOne.softwareName,
saiyouDate: dataOne.saiyouDate,
version: dataOne.version,
shubetu: dataOne.shubetu,
licenseManage: dataOne.licenseManage,
youto: dataOne.youto,
bikou: dataOne.bikou,
authorizer: dataOne.authorizer,
approvalDate: dataOne.approvalDate,
url: dataOne.url
}
axios.post("http://localhost:8080/action/edit/contents", list)
.then(response => {
if (response.data != null) {
alert('編集完了!')
window.setTimeout(() => {
history.push("/")
}, 1000)
}
})
};
return (
<div>
<div className="container">
<Card>
<Card.Header className="text-center" style={{ backgroundColor: '#75A9FF', color: '#FFF' }}>
<FontAwesomeIcon icon={faList} />使用許可ソフトウェアリスト
</Card.Header>
<Form id="listFormId" onSubmit={handleSubmit}>
<Form.Control
required
type="hidden"
name="listNo"
value={dataOne.listNo || ""}
onChange={handleChange}
/>
<Card.Body>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>ソフトウェア名</Form.Label>
<Form.Control
type="text"
name="softwareName"
value={dataOne.softwareName || ""}
onChange={handleChange}
/>
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>採用日</Form.Label>
<Form.Control
type="text"
name="saiyouDate"
value={dataOne.saiyouDate || ""}
onChange={handleChange} />
</Form.Group>
<Form.Group as={Col}>
<Form.Label>バージョン</Form.Label>
<Form.Control
type="text"
name="version"
value={dataOne.version || ""}
onChange={handleChange}
/>
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>種別</Form.Label>
<Form.Control
type="text"
name="shubetu"
value={dataOne.shubetu || ""}
onChange={handleChange} />
</Form.Group>
<Form.Group as={Col}>
<Form.Label>ライセンス</Form.Label>
<Form.Control
type="text"
name="licenseManage"
value={dataOne.licenseManage || ""}
onChange={handleChange} />
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>用途</Form.Label>
<Form.Control
type="text"
name="youto"
value={dataOne.youto || ""}
onChange={handleChange} />
</Form.Group>
<Form.Group as={Col}>
<Form.Label>備考</Form.Label>
<Form.Control
type="text"
name="bikou"
value={dataOne.bikou || ""}
onChange={handleChange} />
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>承認者</Form.Label>
<Form.Control
type="text"
name="authorizer"
value={dataOne.authorizer || ""}
onChange={handleChange} />
</Form.Group>
<Form.Group as={Col}>
<Form.Label>承認日</Form.Label>
<Form.Control
type="text"
name="approvalDate"
value={dataOne.approvalDate || ""}
onChange={handleChange} />
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>URL</Form.Label>
<Form.Control
type="text"
name="url"
value={dataOne.url || ""}
onChange={handleChange} />
</Form.Group>
</Form.Row>
</Card.Body>
<Card.Footer style={{ "textAlign": "right" }}>
<Link to="/">
<Button size="sm" variant="outline-primary" type="button">
<FontAwesomeIcon icon={faHome} />トップ
</Button>
</Link>
<Button size="sm" variant="outline-success" type="submit" style={{ marginLeft: '10px' }} >
<FontAwesomeIcon icon={faClipboardCheck} /> 編集完了
</Button>
</Card.Footer>
</Form>
</Card>
</div>
<Box pt={4} pb={4}>
<Copyright />
</Box>
</div>
)
}
Add react-bootstrap validation
import axios from 'axios'
import { useEffect, useState } from 'react'
import { Card, Form, Button, Col } from 'react-bootstrap';
import React, { Component } from 'react'
import { BrowserRouter, Route, Link, useHistory } from 'react-router-dom'
import Box from '#material-ui/core/Box';
import { Copyright } from './Copyright';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faList, faHome, faClipboardCheck } from '#fortawesome/free-solid-svg-icons'
export const EditData = (props) => {
const [validated, setValidated] = useState(false);//Add
const history = useHistory()
const id = props.listNo;
const [dataOne, setDataOne] = useState({});
useEffect(() => {
axios.get("http://localhost:8080/action/edit/" + id).then((response) =>
setDataOne(response.data)
)
}, [])
const handleChange = event => {
setDataOne({ ...dataOne, [event.target.name]: event.target.value });
};
const handleSubmit = event => {
//Add and Change code
const form = event.currentTarget;
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
setValidated(true);
} else {
const list = {
listNo: dataOne.listNo,
softwareName: dataOne.softwareName,
saiyouDate: dataOne.saiyouDate,
version: dataOne.version,
shubetu: dataOne.shubetu,
licenseManage: dataOne.licenseManage,
youto: dataOne.youto,
bikou: dataOne.bikou,
authorizer: dataOne.authorizer,
approvalDate: dataOne.approvalDate,
url: dataOne.url
}
axios.post("http://localhost:8080/action/edit/contents", list)
.then(response => {
if (response.data != null) {
alert('編集完了!')
window.setTimeout(() => {
history.push("/")
}, 1000)
}
})
}
};
return (
<div>
<div className="container">
<Card>
<Card.Header className="text-center" style={{ backgroundColor: '#75A9FF', color: '#FFF' }}>
<FontAwesomeIcon icon={faList} />使用許可ソフトウェアリスト
</Card.Header>
{// add novalidate and required
}
<Form id="listFormId" noValidate validated={validated} onSubmit={handleSubmit}>
<Form.Control
required
type="hidden"
name="listNo"
value={dataOne.listNo || ""}
onChange={handleChange}
/>
<Card.Body>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>ソフトウェア名</Form.Label>
<Form.Control
required
type="text"
name="softwareName"
value={dataOne.softwareName || ""}
onChange={handleChange}
/>
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>採用日</Form.Label>
<Form.Control
required
type="text"
name="saiyouDate"
value={dataOne.saiyouDate || ""}
onChange={handleChange} />
</Form.Group>
<Form.Group as={Col}>
<Form.Label>バージョン</Form.Label>
<Form.Control
required
type="text"
name="version"
value={dataOne.version || ""}
onChange={handleChange}
/>
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>種別</Form.Label>
<Form.Control
required
type="text"
name="shubetu"
value={dataOne.shubetu || ""}
onChange={handleChange} />
</Form.Group>
<Form.Group as={Col}>
<Form.Label>ライセンス</Form.Label>
<Form.Control
required
type="text"
name="licenseManage"
value={dataOne.licenseManage || ""}
onChange={handleChange} />
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>用途</Form.Label>
<Form.Control
required
type="text"
name="youto"
value={dataOne.youto || ""}
onChange={handleChange} />
</Form.Group>
<Form.Group as={Col}>
<Form.Label>備考</Form.Label>
<Form.Control
type="text"
name="bikou"
value={dataOne.bikou || ""}
onChange={handleChange} />
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>承認者</Form.Label>
<Form.Control
required
type="text"
name="authorizer"
value={dataOne.authorizer || ""}
onChange={handleChange} />
</Form.Group>
<Form.Group as={Col}>
<Form.Label>承認日</Form.Label>
<Form.Control
required
type="text"
name="approvalDate"
value={dataOne.approvalDate || ""}
onChange={handleChange} />
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>URL</Form.Label>
<Form.Control
required
type="text"
name="url"
value={dataOne.url || ""}
onChange={handleChange} />
</Form.Group>
</Form.Row>
</Card.Body>
<Card.Footer style={{ "textAlign": "right" }}>
<Link to="/">
<Button size="sm" variant="outline-primary" type="button">
<FontAwesomeIcon icon={faHome} />トップ
</Button>
</Link>
<Button size="sm" variant="outline-success" type="submit" style={{ marginLeft: '10px' }} >
<FontAwesomeIcon icon={faClipboardCheck} /> 編集完了
</Button>
</Card.Footer>
</Form>
</Card>
</div>
<Box pt={4} pb={4}>
<Copyright />
</Box>
</div>
)
}
I resolved that Error in the following way.
I wrote event.preventDefault(); outside if statement like that.
event.preventDefault();
//Add and Change code
const form = event.currentTarget;
if (form.checkValidity() === false) {
event.stopPropagation();
setValidated(true);
} else {
I have a multistep form for signing up. What is the easiest and the best approach for form validation? What should I use? Is it alright to use Formik for that? Could you suggest to me what to do?
Here is one of the forms:
return(
<Container>
<Row className="justify-content-center">
<Col md="8">
<Card border="dark">
<Card.Title className="text-center">New User</Card.Title>
<Card.Body>
<Form>
<Form.Group controlId="formGridFirstName">
<Form.Label>First Name</Form.Label>
<Form.Control type="text" value={formValues.firstName} onChange={handleChange('firstName')} />
</Form.Group>
<Form.Group controlID="formGridLastName">
<Form.Label>Last Name</Form.Label>
<Form.Control type="text" value={formValues.lastName} onChange={handleChange('lastName')} />
</Form.Group>
<Form.Group controlID="formGridEmail">
<Form.Label>Email</Form.Label>
<Form.Control type="email" value={formValues.email} onChange={handleChange('email')} />
</Form.Group>
<Form.Group controlId="formGridPassword">
<Form.Label>Password</Form.Label>
<Form.Control type="password" value={formValues.password} onChange={handleChange('password')} />
</Form.Group>
<Form.Group controlId="formGridPhone">
<Form.Label>Phone</Form.Label>
<Form.Control type="tel" value={formValues.phone} onChange={handleChange('phone')} />
</Form.Group>
<Button variant="light" type="submit" size="lg" onClick={redirectToHome}>Cancel</Button>
<Button variant="primary" type="submit" size="lg" onClick={saveAndContinue}>Next</Button>
</Form>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
);
};
You can use Formik and a Yup for validation schema:
https://formik.org/docs/api/formik#validationschema-schema----schema
npm links:
https://www.npmjs.com/package/formik
https://www.npmjs.com/package/yup
It will look like this:
export default function MyForm() {
const initValues = {
firstName: "",
lastName: "",
email: "",
password: "",
phone: ""
};
const schema = Yup.object().shape({
firstName: Yup.string(),
lastName: Yup.string(),
email: Yup.string().email("Email format is invalid."),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(/[a-zA-Z]/, "Password can only contain Latin letters."),
phone: Yup.string().phone(" ")
});
return (
<Formik validationSchema={schema} initialValues={initValues}>
{props => {
const {
values,
touched,
errors,
isSubmitting,
handleChange,
setFieldTouched
} = props;
return (
<Container>
<Row className="justify-content-center">
<Col md="8">
<Card border="dark">
<Card.Title className="text-center">New User</Card.Title>
<Card.Body>
<Form>
<Form.Group controlId="formGridFirstName">
<Form.Label>First Name</Form.Label>
<Form.Control
name="firstName"
type="text"
value={values.firstName}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Form.Group controlID="formGridLastName">
<Form.Label>Last Name</Form.Label>
<Form.Control
name="lastName"
type="text"
value={values.lastName}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Form.Group controlID="formGridEmail">
<Form.Label>Email</Form.Label>
<Form.Control
name="email"
type="email"
value={values.email}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Form.Group controlId="formGridPassword">
<Form.Label>Password</Form.Label>
<Form.Control
name="password"
type="password"
value={values.password}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Form.Group controlId="formGridPhone">
<Form.Label>Phone</Form.Label>
<Form.Control
name="phone"
type="tel"
value={values.phone}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Button
variant="light"
type="submit"
size="lg"
onClick={redirectToHome}
>
Cancel
</Button>
<Button
variant="primary"
type="submit"
size="lg"
onClick={saveAndContinue}
>
Next
</Button>
</Form>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
);
}}
</Formik>
);
}
But you should change also your Form.Control component:
Add
import { ErrorMessage, getIn } from "formik";
const error = getIn(props.errors, props.name);
and add to your Input these attributes
onChange={(e) => {setFieldTouched("firstName");handleChange(e);}
error={error}
and Error to show
<Error component="div" name={props.name} />
I would suggest adding your own validators and bundling inputs that are of the same type. For example First Name and Last Name are both strings so you can have a validator for string types. You could do it like this
//validator.js
const stringValidator = (value) => {
if(isEmpty(value)){
return { isValid: false, errors: ["Field cannot be blank"] }
}
return { isValid: true, errors: [] }
}
const config = {
firstName: stringValidator,
lastName: stringValidator,
email: emailValidator,
cellphone: cellphoneValidator,
password: passwordValidator
}
const getValidator = (questionId) => config[questionId];
export default (questionId, value) => {
const validate = getValidator(questionId);
return validate(value)
}
...
// Form.js class
answerQuestion(questionId){
return (e) => {
const answer = e.target.value;
const validation = validate(questionId, answer)
const { errors } = validation;
let { fields } = this.state;
fields[questionId] = { errors, value, label: field.label }
this.setState({ fields })
}
}
...
<Form>
{this.state.fields.map( field => {
return(
<Form.Group>
<Form.Label>{field.label}</Form.Label>
<Form.Control type="text" value={field.value} onChange={this.answerQuestion(field.questionId)} />
{field.errors.map(err => <span>{err}</span>}
</Form.Group>
)
}
</Form>
I'm trying to get the currently logged in users email and use it in my fetch() call. I can currently get the email from getfirstapi() and use it in my form but I'm having trouble passing it into my getSecondApi() where the fetch() is ?
Iv tried creating a getEmail function to return it, but with no luck.
Code
import * as React from "react";
import axios from "axios";
import { Form, Card, Grid } from "tabler-react";
import { Button, Modal } from "semantic-ui-react";
import Auth from '#aws-amplify/auth';
import '../../index.css';
import { arrayOf } from "prop-types";
//const config = require('../config.json');
class GeneralInformation extends React.Component {
constructor(props) {
super(props);
this.state = {
// States from API
firstname: "",
middlename: "",
surname: "",
city: "",
postcode: "",
state: "",
email: "",
about: "",
// States for editable form
formfirstname: "",
formmiddlename: "",
formsurname: "",
formcity: "",
formpostcode: "",
formstate: "",
formabout: "",
// Modal State
open: false,
};
}
getUseremail() {
return Auth.currentAuthenticatedUser().then(user => user.attributes.email)
}
email = Auth.currentAuthenticatedUser().then(user => user.attributes.email)
getFirstApi() {
return Auth.currentAuthenticatedUser().then((user) => {
this.setState({email: user.attributes.email, formemail: user.attributes.email})
});
}
getSecondApi(email) {
fetch(`https://ezha2ns0bl.execute-api.ap-southeast-2.amazonaws.com/prod/userdata?foo=${encodeURIComponent(email)}`)
.then(res => res.json())
.then(
console.log("THIS IS RESULT2 " + email),
(result) => {
this.setState({
firstname: result.Item.userFirstName,
middlename: result.Item.userMiddleName,
surname: result.Item.userLastName,
city: result.Item.userCity,
postcode: result.Item.userPostcode,
state: result.Item.userState,
about: result.Item.userAbout,
formfirstname: result.Item.userFirstName,
formmiddlename: result.Item.userMiddleName,
formsurname: result.Item.userLastName,
formcity: result.Item.userCity,
formpostcode: result.postcode,
formstate: result.Item.userState,
formabout: result.Item.userAbout,
});
console.log("THIS IS RESULT1 " + result)} ,
)
}
componentDidMount() {
this.getFirstApi();
this.getSecondApi();
}
handleChange = (input) => (event) => {
this.setState({ [input]: event.target.value });
};
handleSubmit = async (event) => {
event.preventDefault();
this.setState((prevState) => ({
// If submitting new values, update the state to represent the new data
firstname: prevState.formfirstname,
middlename: prevState.formmiddlename,
surname: prevState.formsurname,
city: prevState.formcity,
postcode: prevState.formpostcode,
email: prevState.formemail,
userState: prevState.formstate,
about: prevState.formabout,
open: false,
}))
try {
const params = {
"userFirstName": this.state.formfirstname,
"userMiddleName": this.state.formmiddlename,
"userLastName": this.state.formsurname,
"userCity": this.state.formcity,
"userPostCode": this.state.formpostcode,
"userEmail": this.state.formemail,
"userState": this.state.formstate,
"userAbout": this.state.formabout,
"userType": "jobseeker"
};
await axios.post('https://qrg3idkox4.execute-api.ap-southeast-2.amazonaws.com/prod/{userEmail}/', params);
}catch (err) {
console.log(`An error has occurred: ${err}`);
}
};
cancelForm = () => {
// If cancelling, reset any fields that have been changed to the original values so that when the modal is re-opened, the old values are shown
this.setState((prevState) => ({
formfirstname: prevState.firstname,
formmiddlename: prevState.middlename,
formsurname: prevState.surname,
formcity: prevState.city,
formpostcode: prevState.postcode,
formstate: prevState.state,
formabout: prevState.about,
open: false,
}));
};
openModal = () => {
this.setState({ open: true });
};
render() {
const {
firstname,
middlename,
surname,
city,
postcode,
state,
email,
about,
formfirstname,
formmiddlename,
formsurname,
formcity,
formpostcode,
formstate,
formabout,
open,
} = this.state;
return (
<div className="card" name="generalInfo">
<Card.Body>
<Grid>
<Grid.Row>
<Grid.Col md={7}>
<Card.Title>General Information</Card.Title>
</Grid.Col>
<Grid.Col md={5}>
{/* MODAL BUTTON */}
<Button
floated="right"
basic
icon="pencil"
type="button"
compact
onClick={this.openModal}
/>
</Grid.Col>
</Grid.Row>
<Grid.Row>
{/* ROW 1 */}
<Grid.Col md={4}>
<Form.Group label="First Name">
<Form.Input name="firstname" readOnly value={firstname} />
</Form.Group>
</Grid.Col>
<Grid.Col md={4}>
<Form.Group label="Middle Name">
<Form.Input name="middlename" readOnly value={middlename} />
</Form.Group>
</Grid.Col>
<Grid.Col md={4}>
<Form.Group label="Surname">
<Form.Input name="surname" readOnly value={surname} />
</Form.Group>
</Grid.Col>
{/* ROW 2 */}
<Grid.Col md={3}>
<Form.Group label="City">
<Form.Input name="city" readOnly value={city} />
</Form.Group>
</Grid.Col>
<Grid.Col md={2}>
<Form.Group label="Post Code">
<Form.Input name="postcode" readOnly value={postcode} />
</Form.Group>
</Grid.Col>
<Grid.Col md={3}>
<Form.Group label="State">
<Form.Input name="state" readOnly value={state} />
</Form.Group>
</Grid.Col>
<Grid.Col md={4}>
<Form.Group label="Email">
<Form.Input name="email" readOnly value={email} />
</Form.Group>
</Grid.Col>
{/* ROW 3 */}
<Grid.Col md={12}>
<Form.Group className="mb=0" label="About Me">
<Form.Textarea
name="aboutme"
rows={3}
disabled
readOnly
value={about}
/>
</Form.Group>
</Grid.Col>
</Grid.Row>
</Grid>
</Card.Body>
{/* MODAL CONTENT */}
<Modal
style={{ position: "relative" }}
closeOnDimmerClick={false}
open={open}
>
<Modal.Header>Edit Info</Modal.Header>
<Modal.Content>
<Form onSubmit={this.handleSubmit}>
<Grid.Row>
<Grid.Col md={4}>
<Form.Group label="First Name">
<Form.Input
name="firstname"
value={formfirstname}
onChange={this.handleChange("formfirstname")}
/>
</Form.Group>
</Grid.Col>
<Grid.Col md={4}>
<Form.Group label="Middle Name">
<Form.Input
name="middlename"
value={formmiddlename}
onChange={this.handleChange("formmiddlename")}
/>
</Form.Group>
</Grid.Col>
<Grid.Col md={4}>
<Form.Group label="Surname">
<Form.Input
name="surname"
value={formsurname}
onChange={this.handleChange("formsurname")}
/>
</Form.Group>
</Grid.Col>
</Grid.Row>
{/* ROW 2 */}
<Grid.Row>
<Grid.Col md={3}>
<Form.Group label="City">
<Form.Input
name="city"
value={formcity}
onChange={this.handleChange("formcity")}
/>
</Form.Group>
</Grid.Col>
<Grid.Col md={2}>
<Form.Group label="Post Code">
<Form.Input
name="postcode"
value={formpostcode}
onChange={this.handleChange("formpostcode")}
/>
</Form.Group>
</Grid.Col>
<Grid.Col md={3}>
<Form.Group label="State">
<Form.Input
name="state"
value={formstate}
onChange={this.handleChange("formstate")}
/>
</Form.Group>
</Grid.Col>
</Grid.Row>
{/* ROW 3 */}
<Grid.Row>
<Grid.Col md={12}>
<Form.Group className="mb=0" label="About Me">
<Form.Textarea
name="aboutme"
rows={3}
value={formabout}
onChange={this.handleChange("formabout")}
/>
</Form.Group>
</Grid.Col>
</Grid.Row>
{/* ROW 4 - SUBMIT */}
<Grid.Row>
<Grid.Col md={12}>
<Button
floated="left"
basic
type="button"
color="red"
onClick={this.cancelForm}
>
{" "}
Cancel{" "}
</Button>
<Button floated="right" basic type="submit" color="green">
{" "}
Accept Changes{" "}
</Button>
</Grid.Col>
</Grid.Row>
</Form>
</Modal.Content>
</Modal>
</div>
);
}
}
export default GeneralInformation;
Form
EDIT
> getEmailApi() { return
> Auth.currentAuthenticatedUser().then((user) => {
> const { attributes = {} } = user;
> console.log(attributes['email']);
> })}
iv created this function to get the email
but don't know how to pass it into the getSecondApi
My Solution
getEmailApi() {
return Auth.currentAuthenticatedUser().then((user) => {
const { attributes = {} } = user;
console.log(attributes['email']);
let email = attributes['email']
return email
})}
getFirstApi() {
return Auth.currentAuthenticatedUser().then((user) => {
this.setState({email: user.attributes.email, formemail: user.attributes.email})
});
}
getSecondApi(email) {
fetch(`https://ezha2ns0bl.execute-api.ap-southeast-2.amazonaws.com/prod/userdata?userEmail=${encodeURIComponent(email)}`)
.then(res => res.json())
.then(
console.log("THIS IS RESULT2 " + email),
(result) => {
this.setState({
firstname: result.Item.userFirstName,
middlename: result.Item.userMiddleName,
surname: result.Item.userLastName,
city: result.Item.userCity,
postcode: result.Item.userPostcode,
state: result.Item.userState,
about: result.Item.userAbout,
formfirstname: result.Item.userFirstName,
formmiddlename: result.Item.userMiddleName,
formsurname: result.Item.userLastName,
formcity: result.Item.userCity,
formpostcode: result.postcode,
formstate: result.Item.userState,
formabout: result.Item.userAbout,
});
console.log("THIS IS RESULT1 " + result)} ,
)
}
BeforDidMount() {
this.getEmailApi().then(email => this.getSecondApi(email)); }
componentDidMount() {
this.BeforDidMount();
this.getFirstApi();
}