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
Related
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.
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'm trying to build a simple form to capture email and password for a new user signing up on Firebase, I'm using React with Typescript and I'm getting the error "Object is possibly 'null'. TS2531" on the following section of the code:
<form onSubmit={(event) => { this.handleSignUp({event, email: this._email.current.value, password: this._password.current.value})}}>
In particular it's the this._email.current.value and this._password.current.value that are throwing this error.
I've dug around about both the error code and type scripting and it's something to do with the "strictNullChecks" on the typescript, but I don't really want to turn that option off, and I don't think I have enough skill or understanding of coding to know how to get around this. Even though I do understand that a form can be submitted with empty values, I am checking later with the firebase auth to make sure that there are strings with more than 4 characters.
Below is the code for the whole react component.
interface IHandleSubmitNewUserFunc {
event: any,
email: any,
password: any
}
class NewUserSignup extends React.Component {
constructor(props: any) {
super(props);
this.handleSignUp = this.handleSignUp.bind(this);
}
handleSignUp(input: IHandleSubmitNewUserFunc) {
input.event.preventDefault();
const { email, password } = input;
if (email.length < 4 && email != null) {
alert('Please enter an email address.');
return;
}
if (password.length < 4 && password != null) {
alert('Please enter a password.');
return;
}
firebase.auth().createUserWithEmailAndPassword(email, password).catch(function (error) {
var errorCode = error.code;
var errorMessage = error.message;
if (errorCode == 'auth/weak-password') {
alert('The password is too weak.');
} else {
alert(errorMessage);
}
console.log(error);
});
}
private _email = React.createRef<HTMLInputElement>();
private _password = React.createRef<HTMLInputElement>();
render() {
return <div>
<div className="signup-modal-container">
<div className="identity-panel">
<img src={logo}></img>
<form onSubmit={(event) => { this.handleSignUp({event, email: this._email.current.value, password: this._password.current.value})}}>
<div className="form-flex-container">
<div className="signup-item">
<h2>
Sign Up
</h2>
<label htmlFor="email" id="email">
Email:
</label>
</div>
<div className="signup-item">
<div className="input-container">
<input type="text" name="email" id="email" ref={this._email}/>
</div>
</div>
<div className="signup-item">
<label htmlFor="password">
Password:
</label>
</div>
<div className="signup-item">
<div className="input-container">
<input type="password" name="password" id="password" ref={this._password}/>
</div>
</div>
<div className="signup-item">
<button type="submit">
Sign Up
</button>
</div>
</div>
</form>
</div>
</div>
<p>
NewUserSignup is showing
</p>
</div>
}
}
export default NewUserSignup;
I'm not really sure what code I'd need to not throw this error, any advice is aprpeciated.
Pretty much as it says. The references are null by default, there's no guarantee that current is assigned by the time you access them (as far as the code knows).
You have 2 options:
Add a truthy check before access this._email.current and this._password.current.
const eCurrent = this._email.current;
const pCurrent = this._password.current;
if (!eCurrent || !pCurrent) {
// This will probably never happen, to respond to events these will be hooked up.
return;
}
Use the non-null assertion since you know it's a safe operation: this._email.current!.value.
// Assert that current won't be null.
const emailValue = this._email.current!.value;
const passwordValue = this._password.current!.value;
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 have a form where I enter an email and confirm email and then continue to the next page and all is well. The validation works fine when the page initially loads and it's the user's first time, so the input field is not prepopulated from cookie data. However, when the user returns, the input field data is prepopulated from cookie data and that is fine but the submit button is still disabled even though the prepopulated text is valid format. I inspected the elements and it seems to think the field is ng-invalid even though it's valid format.
I noticed when I go to one of the fields and backspace to remove the last character and reinsert the same character as before for email and do the same for the next field, the form is valid again. Even though, it's the same text as before.
I'm wondering why validation fails when the form first loads with prepopulated data?
Here's my code:
export class EmailComponent implements OnInit {
public user : User;
Form : FormGroup;
displayErrors : boolean;
ngOnInit() {
// initialize model here
this.user = {
Email: '',
confirmEmail: ''
}
}
constructor(fb: FormBuilder, private cookieService: CookieService, private cryptoService: CryptoService) {
var encryptedEmail = this.cookieService.get(AppCookie.EmailAddress);
var Cookie = null;
if(encryptedEmail != null && encryptedEmail != 'undefined')
Cookie = this.cryptoService.Decrypt(encryptedEmail);
if(Cookie == null) {
this.Form = fb.group({
email: ['', [Validators.required, Validators.pattern(EMAIL_REGEXP)]],
confirmEmail: ['', [Validators.required, Validators.pattern(EMAIL_REGEXP)]]
},
{
validator: this.matchingEmailsValidator('email', 'confirmEmail')
});
}
else {
this.Form = fb.group({
email: [Cookie, [Validators.required, Validators.pattern(EMAIL_REGEXP)]],
confirmEmail: [Cookie, [Validators.required, Validators.pattern(EMAIL_REGEXP)]]
},
{
validator: this.matchingEmailsValidator('email', 'confirmEmail')
});
}
}
save(model: User, isValid: boolean)
{
model.Email = this.Form.get('email').value;
var encrypted = this.cryptoService.Encrypt(model.Email);
this.cookieService.put(AppCookie.EmailAddress, encrypted);
}
matchingEmailsValidator(emailKey: string, confirmEmailKey: string): ValidatorFn {
return (group: FormGroup): {[key: string]: any} => {
let email = group.controls[emailKey];
let confirmEmail = group.controls[confirmEmailKey];
if (email.value !== confirmEmail.value) {
return {
mismatch: true
};
}
};
}
}
and here's my view:
<form [formGroup]="Form" novalidate (ngSubmit)="Form.valid && save(Form.value, Form.valid)">
<div class="login-wrapper">
<div class="login-page">
<section class="login-form form-group">
<p>
<input id="email"
[class.email-address-entry]="!displayErrors"
[class.email-address-entry-text]="!displayErrors && this.Form.get('email').value !='' "
type="email"
placeholder="name#domain.com" formControlName="email" />
</p>
<p class="login-form__msg">Reenter your email to confirm</p>
<input id="reenteremail"
[class.email-address-entry]="!displayErrors"
[class.entry-border-invalid]="displayErrors && !Form.valid && Form.errors?.mismatch"
[class.email-address-entry-text]="!displayErrors && this.Form.get('email').value !='' "
(blur)="displayErrors=true"
type="email" placeholder="name#domain.com"
formControlName="confirmEmail"/>
<p class="error-msg" *ngIf="displayErrors && !Form.valid && Form.errors?.mismatch">The email you entered does not match.</p>
</section>
<p class="login-confirm">
<span>
<button type="submit" [disabled]="!Form.valid" (click)="Form.get('email').length > 0 ? save(Form.value, Form.valid) : NaN">Confirm</button>
</span>
</p>
</div>
</div>
</form>
EDIT: It's similar to this issue as well:
Angular 2 - Form is invalid when browser autofill
I tried adding this:
ngAfterViewChecked() {
if (Cookie) {
// enable to button here.
var element = <HTMLInputElement> document.getElementById("confirmBtn");
element.disabled = false;
}
But it won't work because fields are still invalid. I need a way to manually set re-validation or change ng-invalid to ng-valid.
If you keep a reference to the form instance (either by using reactive forms or by accessing it using #ViewChild) you should be able to write the following in ngAfterViewInit():
for (var i in this.form.controls) {
this.form.controls[i].updateValueAndValidity();
}
Or perhaps marking the fields as touched will be better in your case:
for (var i in this.form.controls) {
this.form.controls[i].markAsTouched();
}