I'm using yup for validating my react form.
My problem is :
I have the below schema for validating an input field.
object().shape({
firstname: string()
.required('First Name is required')
.test('length', 'First Name must have more than 1 character', (value) => {
console.log(value && value.length < 2 ? true : false);
return value && value.length < 2 ? true : false;
})
.test('alphabets', 'Name must only contain alphabets', (value) => {
console.log(!/^[A-Za-z]+$/.test(value));
return !/^[A-Za-z]+$/.test(value);
})
});
When I enter a single character it shows Name must only contain alphabets error message and when I try to type more characters it shows First Name must have more than 1 character error message.
What should I do wrong?
Anyone, please help me with this?
You seem to be doing both of your validation wrong way, you want to return true if validation passes and false if validation fails.
In your first validation value && value.length < 2 ? true : false you are looking for value.length > 2 instead of < and also no need to have ternary as comparison operator will return true/false value after evaluation.
In your second validation !/^[A-Za-z]+$/.test(value); you are negating the validation by using !
Here's the corrected validation code:
object().shape({
firstname: string()
.required('First Name is required')
.test('length', 'First Name must have more than 1 character', (value) => {
return value && value.length > 2;
})
.test('alphabets', 'Name must only contain alphabets', (value) => {
return /^[A-Za-z]+$/.test(value);
})
});
Related
In my return statement, I try to check for a valid number or else assign the value 0. This doesn't seem to work, is there a different way of doing this?
return (
<Input
color="teal"
size="regular"
outline={true}
type={question?.type}
name={question?.name}
value={value ?? ''}
placeholder={question?.placeholder ?? ''}
onChange={(e: React.FormEvent<HTMLInputElement>) => {
if (question?.type === 'number' && Number(e.currentTarget.value) < 1) {
e.currentTarget.value = 0;
}
dispatch(
setResponseGeneric({
property: question?.name,
value: e.currentTarget.value,
})
);
}}
></Input>
);
This is because Number('bad input') returns NaN (Not a Number). NaN is a value which is not smaller or greater than 1. You should change your condition so that it handles those scenarios.
if (question?.type === 'number' && (isNaN(e.currentTarget.value) || Number(e.currentTarget.value) < 1)) {
Also something else, besides your question, changing the element value like you do in here e.currentTarget.value = 0; is bad practice since you're changing it imperatively. It's better to make sure you're changing the state so that the value variable becomes 0 (I'm not sure if that already happens in setResponseGeneric).
Possible to validate multiple emails seperated by commas with react-hook-form .
So I have a material-ui text field which is uses react-hook-form for the validation..
Initial the input field takes a single email and it's being validated by the react-hook-form.
currently I want the user to be able to enter multiple emails separated by commas and also validates each one of them.
Currently what I'm able to do is validate when the user clicks on submit but , I want to be able to validate when the user is typing the emails.
<TextField
onChange={(e) => {
validateRecipientEmail(e.target.value);
}}
name='recipientEmail'
placeholder='sender#email.com'
fullWidth
inputRef={register({
required: true,
})}
error={errors.recipientEmail && true}
/>
{errors.recipientEmail && (
<Typography variant='caption' className={Type.textError}>
Invalid email address
</Typography>
)}
I found a way to kind of achieve the desire goal by doing the validation with onChange event listener.
but after doing the validation and populating the error on the screen. the error disappear when the input field is not focus.
Below is my validation
const validateRecipientEmail = (value) => {
let currentEmails = value.split(',').filter((e) => e && e.trim());
let regex = /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]+$/i;
for (let i = 0; i < currentEmails.length; i++) {
if (!regex.test(currentEmails[i].replace(/\s/g, ''))) {
setrecipientEmailErrorMessage(
`Enter valid Email(s) seperated by comma (,)`
);
setError('recipientEmail', {
type: 'manual',
});
}
}
if (currentEmails.length > 10) {
setrecipientEmailErrorMessage(`Emails should not be more than 10`);
setError('recipientEmail', {
type: 'manual',
});
}
};
so i found out you can pass a function to validate attribute which solves my problem
<TextField
name='recipientEmail'
placeholder='Eg. recipient#email.com'
inputRef={register({
required: true,
validate: {
validEmail: (value) => {
let currentEmails = value
.split(',')
.filter((e) => e && e.trim());
let regex = /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]+$/i;
for (let i = 0; i < currentEmails.length; i++) {
if (!regex.test(currentEmails[i].replace(/\s/g, ''))) {
return false;
}
}
},
emailLength: (value) => {
let currentEmails = value
.split(',')
.filter((e) => e && e.trim());
if (currentEmails.length > 10) {
return false;
}
},
},
})}
error={errors.recipientEmail && true}
fullWidth
/>
"validEmail" validate the email and
"emailLength" validate the email length
Thank You
Want to trim an input field when user onBlurs.
<Controller
...
onBlur={([e]) => {
const { value } = e.target;
const trimmedValue = value.trim();
console.log('trim here: ', value, value.length, trimmedValue.length);
if (trimmedValue === '') {
console.log('error!!!');
return trimmedValue;
}
return trimmedValue;
}} />
Rule:
rules={{
pattern: {
value: new RegExp(firstName.validationString, 'i'),
message: 'First name must be 2 - 100 characters with no numbers.',
},
required: firstName.mandatory && 'Must fill in first name',
}}
The function is triggered and reaching the if-statement. But is not triggering an error even though I have a rule set as required.
use this in your register or controller
validate: (value) => { return !!value.trim()}
Take a look here: https://github.com/react-hook-form/react-hook-form/issues/1650
I am trying to implement a checkout form in React. The form has 4 fields in all: Name, CC Number, CC expiration and CVV. I am using a library that validates each field on unfocus. The validation is triggered by the validationCallback method which takes 3 arguments: field, status, and message. I'd like to key off of the status for each input and only allow submit once each status === true. Here is my code.
constructor(props) {
super(props);
this.state = {
nameOnCard: '',
errorMessage: '',
showLoaderForPayment: '',
collectJs: null,
token: null,
isPaymentRequestCalled: false,
showErrorModal: false,
paymentErrorText: '',
disabled: true,
};
}
I have a disabled property in my state which I'm initially setting to true.
validationCallback: (field, status, message) => {
if (status) {
this.setState({ errorMessage: '' });
} else {
let fieldName = '';
switch (field) {
case 'ccnumber':
fieldName = 'Credit Card';
break;
case 'ccexp':
fieldName = 'Expire Date';
break;
case 'cvv':
fieldName = 'Security Code';
break;
default:
fieldName = 'A';
}
if (message === 'Field is empty') {
this.setState({ errorMessage: `${fieldName} ${message}` });
} else {
this.setState({ errorMessage: `${message}` });
}
}
},
In the above method, I'd like to set disabled to false if each of the field's status===true... Below is the button which I'm setting to be the value of this.state.disabled.
<button
className="continueBtn disabled"
disabled={this.state.disabled}
onClick={this.handleCardSubmit}
>
<span className="fa fa-lock" />
Pay $
{selectedPayment.amount}
</button>
I hope this is enough of the code to help with the issue. I can provide more of the file if need be.
From what i understand, you want to set the button to NOT DISABLED if all the fields are filled properly, i.e. all status are true.
What you can do is maintain a boolean array for each field and update the status in that array, i.e. initialize an array of length = no. of fields (in your case 3) and set all values as false. False depicts that the field hasn't been validated.
this.state = {
statusArray = [false, false, false] // For as many fields
}
Then in validationCallback, set the index as true or false for that field i.e. if the 2nd field status is returned true by your validation library, set statusArray as [false, true, false].
The form will only be validated if all 3 of the values become true. So you can iterate over the array and check if array has all 3 values as true. or you can use the logical AND operator which returns true only if all values are true(the approach which i use below).
For the button,
<button disabled={this.checkDisable()}>
checkDisable = () => {
let temp = this.state.statusArray;
let answer = true;
for(int i=0;i<temp.length;i++)
answer = answer && temp[i];
return answer; // Only returns true if all 3 values are true
}
I hope you get it now.
You need to check 2 things, has the form been touched and are there any errors. I don't know what library you are using but most likely it has a property touched in it, if not add an onFocus to each input field and a touched property in your state. You don't really need a disabled property in your state since its a computed value. Just check on every render if the form has been touched and if there are any errors.
state = {
...,
touched: false,
...
}
handleFocus = () => this.setState({touched: true})
render(){
const disabled = !!(this.state.touched && this.state.errorCode)
return(
...
<input onFocus={this.handleFocus} ... />
...
<button disabled={disabled}
)
}
EDIT:
state = {
...
validInputs: []
}
validationCallback: (field, status, message) => {
if (status) {
this.setState((state) => ({ errorMessage: '', validInputs: [... new Set([...state.validInputs, field])] }));
} else {
...
render(){
const disabled = this.state.length < inputs.length // the number of the input fields
return(
...
<button disabled={disabled} >
...
)
I have dynamically added fields on click.
addNewFiled() {
let parent = this;
this.scope.fields.push({
key: 'field-'+parent.scope.fields.length,
type: 'horizontalInput',
templateOptions: {
placeholder :'Enter Field',
label: 'Filed',
required: false
},
validators: {
fieldFormat: function($viewValue, $modelValue, scope) {
let value = $viewValue;
if(value.length != 12){
scope.to.message = "Field should be 12 characters";
return false;
}
return true;
}
}
});
}
What I need is to validate the the value entered is not in another field in the validator, I tried looping through the model but its not efficient, any help is appreciated.
I have encountered this case once, I solved the issue using 2 maps
Basically, you will have 2 maps, one which will contain the index of the field mapped to the value of it, the second map will contain the value of the field mapped to the number of the repetitions of that value
In your validator, you decrement the number of repetitions for the previous value ( after done with other validations) and increase the number of repetitions of the new value and check if it's more than 1 then it's repeated.
In your Dialog define the two maps
private valuesMap: any = [];
private keysArray:any = [];
In your field, you inject a controller to save the index of the current field
controller: function ($scope) {
$scope.index = parent.scope.fields.length-1;
parent.keysArray[$scope.index] = $scope.index;
},
then in your validator
if(value) {
if(angular.isDefined(parent.valuesMap[parent.keysArray[scope.index]])) {
parent.valuesMap[parent.keysArray[scope.index]]= parent.valuesMap[parent.keysArray[scope.index]] -1;
}
parent.keysArray[scope.index] = value;
if(angular.isDefined(parent.valuesMap[value]) && parent.valuesMap[value] > 0) {
parent.valuesMap[value] = parent.valuesMap[value]+1;
scope.to.message = "Value is already entered";
return false;
}
parent.valuesMap[value] = 1;
}
Hope this works with your scenario
You don't need a validator on this, there is already default validation for field length through the minlength and maxlength properties in the templateOptions.
Simply do this:
templateOptions: {
placeholder :'Enter Field',
label: 'Filed',
required: false,
minlength: 12,
maxlength: 12
},