I am trying to handle validation errors of my laravel API with rect.
A strange thing happens to me when I try to validate the form for submitting data, it is as if the states of react are not updated correctly.
This is the code I use for validation process.
I have react states for handling error messages for each field in the form:
const [errors,setErrors] = useState({
'name': null,
'email': null,
});
useEffect(() => {
console.log(errors)
}, [errors]);
const handleErrors = (messages) => {
// {"name":["Inserire il nome azienda"],"email":["The email must be a valid email address."]}
Object.keys(messages).forEach(el => {
console.log("Element: " + el)
console.log("Message: " + messages[el][0])
setErrors({
...errors,
[el]: messages[el][0]
})
});
}
const handleSubmit = () => {
flushSync(() => {
setErrors({
'name': null,
'email': null,
});
})
api.post("organization", organization)
.then(response => {
})
.catch(reason => {
if (reason.response.status === 422)
{
handleErrors(reason.response.data.errors);
}
});
}
The application requires that the name field be mandatory, while the email field is either null or a valid email.
This is the console output with both fields blank (correct):
// this is from use effect print
{name: null, email: null}
POST http://localhost/api/v2/organization 422 (Unprocessable Content)
add.jsx:70 Element: name
add.jsx:71 Message: Inserire il nome azienda
// this is from use effect print
add.jsx:51 {name: 'Inserire il nome azienda', email: null}
This is from with a valid name and an INVALID email:
// this is from use effect print
{name: null, email: null}
POST http://localhost/api/v2/organization 422 (Unprocessable Content)
add.jsx:70 Element: email
add.jsx:71 Message: The email must be a valid email address.
// this is from use effect print
add.jsx:51 {name: 'Inserire il nome azienda', email: 'The email must be a valid email address.'}
It appears that the state is maintained with the previous one.
Related
I have a code to do login task:
onSubmit() {
this.$q.loading.show();
this.$axios.post("http://localhost/adminision/api/usermanager/login.php",
{
language: "vi",
instituteid: this.$store.state.mainState.istituto,
username: this.username,
password: this.passwd
}).then((t => {
1 == t.data.success ? (this.$store.commit("mainState/setLoginData", t.data.token), this.$store.commit("mainState/setFirstName", t.data.firstName), this.$store.commit("mainState/setLastName", t.data.lastName), this.$store.commit("mainState/setMatrixID", "login"), this.$q.sessionStorage.set("token", t.data.token), this.$router.push({
path: "/dashboard"
})) : (this.$q.loading.hide(), this.validatormessage = "Username or password not valid", this.mostraerrori = !0)
})).catch((t => {
this.validatormessage = t.response.data.error.description, this.mostraerrori = !0
}))
},
But when i click submit button, this.$axios.post runs, but nothing happens. No login.php appear in Network tab of Chrome browser, with no js error. I've set breakpoint in catch body but it is unreachable.
So, please help, what can i do ?
You have syntax error in your code. Each line should have been ended with semicolon instead of comma or If you leave it javascript ASI will auto insert it:
onSubmit() {
this.$q.loading.show();
this.$axios.post("http://localhost/adminision/api/usermanager/login.php", {
language: "vi",
instituteid: this.$store.state.mainState.istituto,
username: this.username,
password: this.passwd
}).then((t => {
if (t.data.susccess === 1) {
this.$store.commit("mainState/setLoginData", t.data.token);
this.$store.commit("mainState/setFirstName", t.data.firstName);
this.$store.commit("mainState/setLastName", t.data.lastName);
this.$store.commit("mainState/setMatrixID", "login");
this.$q.sessionStorage.set("token", t.data.token);
this.$router.push({
path: "/dashboard"
})
} else {
this.$q.loading.hide();
this.validatormessage = "Username or password not valid";
this.mostraerrori = !0;
}
})).catch((t) => {
this.validatormessage = t.response.data.error.description;
this.mostraerrori = !0;
})
},
I have created a react app to take user inputs.
class VehiReg extends Component {
constructor(props){
super(props);
this.state={
vehicle:"",
plateNo:"",
owner:"",
manufacturer:"",
manufacturedYear:"",
color:""
}
}
takInput = (e) => {
const {name,value}= e.target;
this.setState({
...this.state,
[name]:value
})
}
register = (e) =>{
e.preventDefault();
const {plateNo,owner,manufacturer,manufacturedYear,color,vehicle} = this.state;
const data ={
plateNo:plateNo,
owner:owner,
manufacturer:manufacturer,
manufacturedYear:manufacturedYear,
color:color,
vehicle:vehicle
}
console.log(data);
axios.post("http://localhost:8080/registrations/new",data).then((res)=>{
if(res.data.success){
this.setState({
vehicle:"",
plateNo:"",
owner:"",
manufacturer:"",
manufacturedYear:"",
color:""
})
}
})
}
<div className="box">
<input placeholder="Enter the licence plate number "
className="input2" name="plateNo" value={this.state.plateNo} onChange={this.takInput} />
</div>
then I want to validate the input which the user enters.
The vehicle license plate can be in many forms:
· Vintage: 13 ශ්රී 9999
· Old: 250-9999, 19-9999
· Modern: WP GA-9999, CAR-9999
then according to the type which the user enters I want to write a function in the backend to validate and categorize the user inputs.
Then again I want to return the vehicle type to the frontend
the main thing that I want to know is how can I validate the user input in the backend.
here is the code I have written for entering the data into the database, before entering the data into the database that validation function should be executed.
//new registration
router.post("/registrations/new",(req, res)=>{
let newRegistration = new Registrations(req.body);
newRegistration.save((err)=>{
if(err){
return res.status(400).json({
error:err
})
}
return res.status(200).json({
success:"registration Ok"
});
});
});
There are different types of doing validations and I will share one which I have used hope this can help you.
In Frontend:
add properties to state:
this.state = {
plateNo: '',
plateNoError: ''},
plateNoValid: false,
}
takInput = (e) => {
const {plateNo,value}= e.target;
const plateNoRegEx =
`^[A-Z]{2}[ -][0-9]{1,2}(?: [A-Z])?(?: [A-Z]*)? [0-9]{4}$`
if(!value){
this.setState({
...this.state,
[plateNoError]:"Plate number cannot be empty"
})
}else if(!plateNo.match(plateNoRegEx)){
this.setState({
...this.state,
[plateNoError]:"Invalid Plate Number"
})
}else{
this.setState({
...this.state,
[plateNo]: value,
[plateNoError]:''
})
}
}
<div className="box">
<input placeholder="Enter the licence plate number "
className="input2" name="plateNo" value=
{this.state.plateNo} onChange={this.takInput} />
{this.state.plateNoError &&
<p>{this.state.plateNoError}</p>
}
</div>
For backend
you can use
1)Express validator - npm install --save express-validator
2)Joi - https://joi.dev/ - npm i joi
both are good for validating requests and you can follow the (route - controller -service) format, where you can write validations in route.
eg: router
.route('/registrations/new')
.post(validate(publicValidation.getUserByEmail),
publicController.getUserByEmail)
const getUserByEmail = {
body: Joi.object().keys({
email: Joi.string().required(),
.....
})
};
I have a simple setup for validating a few form entries. It only pushes validation for the first entry. So in state I have:
this.state = {
username: '',
email: '',
zip: '',
errors:[]
}
The submit button:
<div>
<button onClick={this.handleSubmit}
>submit
</button>
</div>
The submit button and the form both trigger handleSubmit:
handleSubmit(e) {
e.preventDefault();
this.setState({errors:[]});
const {username, email, zip} = this.state;
const errors = validate(username, email, zip);
if (errors.length>0) {
this.setState({errors});
}
}
which hits validate:
function validate(username, email, zip) {
let validEmail = RegExp(/[^# \t\r\n]+#[^# \t\r\n]+\.[^# \t\r\n]+/);
let validZip = RegExp(/^\d{5}$/);
const errors = [];
if (username.length < 3 || null) {
errors.push('Name must be at least 3 characters long.');
}
if (validEmail.test(email)) {
errors.push('Email address must be valid email address.');
}
if (validZip.test(zip)) {
errors.push('Zip code must be 5 digits.')
}
console.log(errors);
return errors;
}
and then, from the updated state, errors are supposed to print as line items in:
<ul className="errorList">
{this.state.errors.map(
(error =>
<li key={error}>
{error}
</li>
)
)}
</ul>
but only the first validation is working (for username), in a console log and in the unordered list.
Looking for advice ^~^
validEmail.test(email) return false, try !validEmail.test(email) will return true if email failed
I am using formsy-react to handle validation of my input fields. Problem is that I can't update states. I followed example on formsy github page and validation is working but problem is that states are incorrect. They are always one step (or few) behind and I am not sure why...
I used callback on setState function to implement some custom logic on validation and that part doesn't work properly.
I have a situation where user enters email. After user enters email I check if email is already registrated. If user is already in system, I create new input component (password type) and if not I create new "input type email" component.
Since all forms elements are required I added one more validation check that checks if new password or email component is added and if there is any data.
To update states I used Forms form API call onChange() and this part is not working for unknown reason.
Does someone knows where is the problem?
This is code I am using:
Component Input (shorted version)
changeValue(event) {
this.setValue(event.currentTarget.value);
},
render() {
// Set a specific className based on the validation
// state of this component. showRequired() is true
// when the value is empty and the required prop is
// passed to the input. showError() is true when the
// value typed is invalid
const className = (this.props.className || "col-md-4" );
const classValidationName =this.isValid() ? 'valid' : this.showError() ? ' invalid' : null;
// An error message is returned ONLY if the component is invalid
// or the server has returned an error message
const errorMessage = this.getErrorMessage();
return (
<div className= {className}>
<div className="md-form">
<span className="prefix"><i className={this.props.icon}></i></span>
<input
className={classValidationName}
name={this.props.name}
id={this.props.id}
type={this.props.inputType}
value={this.getValue() || ""}
onChange={this.changeValue}
onBlur={this.props.controlFuncOnBlur}
placeholder={this.props.placeholder}
required={this.props.required}
pattern={this.props.pattern}
/>
<label id={this.props.name + 'Label'} htmlFor={this.props.name} data-error={errorMessage}
data-success={this.props.successMessage}>{this.props.title}
</label>
</div>
</div>
);
}
Container (shorted version)
handleEmailBlur(event) {
const self = this;
if (this.refs.email.isValid) {
axios.get('/api/checkIsUserRegistrated', {
params: {
email: this.state.email
}
})
.then(function (response) {
if (self.state.userExist !== response.data[0].userExist) {
self.setState({
userExist: response.data[0].userExist,
confirmEmail: "",
password: ""
});
self.forceUpdate();
}
})
.catch(function (error) {
console.log(error);
});
}
}
enableButton = () => {
this.setState({
formValid: true
});
}
disableButton = () => {
this.setState({
formValid: false
});
}
saveCurrentValuesToStates = (getCurrentValues, isChanged) => {
console.log(this);
this.setState(getCurrentValues, ()=> {
if (this.state.formValid && (this.state.password || this.state.confirmEmail)){
this.setState({
canSubmitForm: true
});
}
else{
this.setState({
canSubmitForm: false
});
}
});
}
<Formsy.Form className="booker-form" ref="form"
onChange={this.saveCurrentValuesToStates} onValid={this.enableButton} onInvalid={this.disableButton}>
<SingleInput
inputType={'email'}
icon={'icon-Email'}
id={'email'}
name={'email'}
title={'E-mail'}
ref="email"
controlFuncOnBlur={this.handleEmailBlur}
content={this.state.email}
errorMessage={'Incorect E-Mail address'}
required
validations="isEmail"
validationError="This is not a valid email"
/>
{(this.state.userExist === '0') ?
<SingleInput
inputType={'email'}
icon={'icon-Email'}
id={'confirmEmail'}
name={'confirmEmail'}
title={'Confirm your E-mail'}
content={this.state.confirmEmail}
required
validations="equalsField:email"
validationError="Emails don't match"
/>
: null}
{(this.state.userExist === '1') ?
<SingleInput
inputType={'password'}
icon={'icon-Padlock'}
id={'password'}
name={'password'}
title={'Enter your password'}
content={this.state.password}
required
/>
: null}
I'm attempting to create a Mongo document then update the document form a form to have additional properties, one of which has an array of objects.
I'm able to save everything except objects to the address array.
The following code snippets show my current attempt to save an object to the address array. I feel like I'm missing a push or shift which I've tried and can't seem to get syntax correct.
Mongoose Schema:
var UserSchema = new mongoose.Schema({
username: { type: String, lowercase: true }
, password: { type: String }
, email: { type: String, lowercase: true }
, phone: { type: String }
, newsletter: Boolean
, created: { type: Date, default: Date.now }
, address: [{
nickname: { type: String }
, streetAddress: { type: String }
, streetAddress2: { type: String }
, state: { type: String }
, zip: { type: String }
}]
});
Model Methods:
First I create an account. The form only asks for username, email, password then redirects to the jade file where users can fill out the rest of the form.
module.exports = exports = function(){
//create account
this.createAndSave = function (req, res ) {
new User({
username: req.body.username
, password: req.body.password
, email: req.body.email
, phone: req.body.phone
, address: [{
nickname: req.body.nickname
, streetAddress: req.body.streetAddress
, streetAddress2: req.body.streetAddress2
, state: req.body.state
, zip: req.body.zip
}]
}).save(function (err, user){
if (err) throw err;
req.session.isLoggedIn = true;
req.session.user = user.username;
res.redirect('/account/' + user.username)
})
}
//update account
this.updateRequest = function (req, res) {
User.update({username: req.user.username}, {
username: req.body.username
, email: req.body.email
, phone: req.body.phone
, newsletter: req.body.newsletter
, address: [{
nickname: req.body.nickname
, streetAddress: req.body.streetAddress
, streetAddress2: req.body.streetAddress2
, state: req.body.state
, zip: req.body.zip
}]
}, function (err) {
res.redirect("/account/" + req.body.username);
});
}
Jade Template: (I'm sure this could be cleaner)
h1 Edit User
#{user}
form(method="POST", action="/account/#{user.username}")
input(type="hidden", name="_method", value="PUT")
.form-group
label(for="username") Name
input#name.form-control(type="text", name="username", value= user.username )
.form-group
label(for="email") Email
input#email.form-control(type="email", name="email", value= user.email )
.form-group
label Phone
input#phone.form-control(type="text", name="phone", value= user.phone )
.form-group
label Newsletter Opt In/Out
input#newsletter(type="checkbox", name="newsletter", checked=(true===false ? "checked" : undefined))
if(user.address.length > 0)
for (var i = 0; i < user.shippingAddresses.length; i++) {}>)
.form-group
label Street Address
input#address.form-control(type="text", name="streetAddress", value= user.shippingAddresses[i].streetAddress )
.form-group
label Address Continued
input#address2.form-control(type="text", name="streetAddress2", value= user.shippingAddresses[i].streetAddress2 )
.form-group
label Zip Code
input#zip.form-control(type="text", name="zip", value= user.shippingAddresses[i].zip )
else
.form-group
label Location Nick Name
input#address.form-control(type="text", name="nickname", value= )
.form-group
label Street Address
input#address.form-control(type="text", name="streetAddress", value= )
.form-group
label Address Cont.
input#address2.form-control(type="text", name="streetAddress2", value= )
.form-group
label State
input#state.form-control(type="text", name="state", value= )
.form-group
label Zip Code
input#zip.form-control(type="text", name="zip", value= )
button(type="submit") Update Account
Additionally there is another address only form which is why the address is an array.
Any direction would be very helpful as I may go unhinged at any moment. If you any further code let me know.
Something else to note, I'm not able to get any of the updated data from the update function to save to mongo.
Thanks!
Here is the solution I came up with. I find the document to update and push an object to the property that stores the array.
Example method:
this.addAddress = function (req, res) {
var newAddress = {
nickname: req.body.nickname,
streetAddress: req.body.streetAddress,
streetAddress2: req.body.streetAddress2,
state: req.body.state,
zip: req.body.zip
}
User.update({username: req.session.user}, { $push : {
address: newAddress
}}, {upsert: true}, function ( err ) {
if(err){
console.log(err);
}else{
console.log("Successfully added");
}
})
}