Error using validate.js on React Native - Unknown validator minimum - reactjs

I am using validate.js from http://validatejs.org/ on react native log in screen.
import React, { Component } from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity } from 'react-native';
// Validate.js validates your values as an object
import validate from 'validate.js'
const constraints = {
email: {
presence: {
message: "Cannot be blank."
},
email: {
message: 'Please enter a valid email address'
}
},
password: {
presence: {
message: "Cannot be blank."
},
length: {
minimum: 5,
message: 'Your password must be at least 5 characters'
}
}
}
const validator = (field, value) => {
// Creates an object based on the field name and field value
// e.g. let object = {email: 'email#example.com'}
let object = {}
object[field] = value
let constraint = constraints[field]
console.log(object, constraint)
// Validate against the constraint and hold the error messages
const result = validate(object, constraint)
console.log(object, constraint, result)
// If there is an error message, return it!
if (result) {
// Return only the field error message if there are multiple
return result[field][0]
}
return null
}
export default class Login extends Component {
state = {
email: '',
emailError: null,
password: '',
passwordError: null,
}
logIn = () => {
let { email, password } = this.state;
console.log( email, password)
let emailError = validator('email', email)
let passwordError = validator('password', password)
console.log( emailError, passwordError)
this.setState({
emailError: emailError,
passwordError: passwordError,
})
}
render() {
const {emailError, passwordError } = this.state
return (
<View>
<TextInput
onChangeText={(email) => this.setState({email})}
placeholder="Email Address"
keyboardType='email-address'
autoCapitalize='none'
/>
<Text> {emailError ? emailError : null }</Text>
<TextInput
onChangeText={(password) => this.setState({password})}
placeholder="Password"
secureTextEntry={true}
autoCapitalize='none'
type="password"
/>
<Text> {passwordError ? passwordError : null }</Text>
<TouchableOpacity onPress={this.logIn}>
<Text>LOG IN</Text>
</TouchableOpacity>
</View>
);
}
}
Running the code above logs the following and throws an error "Unknown validator message"
.. I ReactNativeJS: 't#t.cl', 'j'
.. I ReactNativeJS: { email: 't#t.cl' }, { presence: { message: 'Cannot be blank.' },
.. I ReactNativeJS: email: { message: 'Please enter a valid email address' } }
.. E ReactNativeJS: Unknown validator message

Your validate call is wrong. It should be:
const result = validate(object, { [field]: constraint })
Note that your object is:
{email: ...}
therefore the constraints passed to validation also need to be of the following form:
{email: emailConstraints }
What happens is that the validator looks into email and looks for validators (constraints) there but what it finds is only message and it prints
Unknown validator "message"
("message" being the name of the unknown constraint).

Try this instead, I have changed in below function and it works.
const validator = (field, value) => {
// Creates an object based on the field name and field value
// e.g. let object = {email: 'email#example.com'}
let object = new Object()
object[field] = value
let constraint = new Object()
constraint[field] = constraints[field]
console.log(object, constraint)
// Validate against the constraint and hold the error messages
const result = validate({}, constraint)//
if (value != '' && value != null) {//if null value it will return with the presence validation
result = validate(object, constraint)
}
// If there is an error message, return it!
if (result) {
// Return only the field error message if there are multiple
return result[field][0]
}
return null
}

I managed to make it work by making it like this:
let emailConstraints;
<TextInput
onChangeText={(emailConstraints) => this.setState({email: emailConstraints })}
placeholder="Email Address"
keyboardType='email-address'
autoCapitalize='none'
/>
<Text> {emailError ? emailError : null }</Text>

I had this specific problem but I could tell what the issue was. My error was Unknown validator checked. The problem is indicated by the word checked in that error message. It means that the attribute checked(in my case) and minimum(in your case) is used in a wrong place in the json of constraints. OR, it is in the right json block but one or many of the other attributes in that block should not be there and the validate.js parser reports the error using the first attribute in that block regardless of it being in the correct one. I had this
policy: {
presence: true,
checked: true}
for checkbox validation, but the library does not allow unknown attributchecked in that block. In your case, I think the message attribute should not be in the length block but validate.js reports the error using the first attribute minimum in that block which is otherwise in the right place. So you should possibly consider removing message attribute and things will be fine.

Related

react-hook-form's validate function always returns errors

This is the one that occurs problem.
<input
type="text"
name="nickname"
{...register('nickname', {
required: true,
validate: async (value) =>
value !== profile?.nickname &&
(await validateNickname(value).catch(() => {
return 'Invalid nickname.';
})),
})}
placeholder="Nickname"
/>
If the input value is not same with the defaultValue
and It's duplicated with another nickname, (validateNickname function returns error)
Then, error message is registered 'Invalid nickname.'
and the errors object looks like this below.
{
nickname:
message: "Invalid nickname."
ref:...
type: "validate"
}
but the problem is
If I input the value which is same with the defaultValue,
or If I input not duplicated value,
The errors object should be empty.
but it still returns error like this below.
{
nickname:
message: ""
ref:...
type: "validate"
}
so there's no error message registered, but somehow, error is exist.
Please let me know if there's anything I'm doing wrong.
Thanks!
This could be the problem:
(await validateNickname(value).catch(() => {
return 'Invalid nickname.';
}))
You want to return a boolean in your AND expression, but you are returning a string, which would be saying the validation is correct.
If you want to handle that validation error so as to display the message in html you can do a simple error handling approach like this one (using named validations):
// ... your validation set up
validate:{
validateNickname_ : async (value) => {
let response = false;
await validateNickname(value)
.then( () => { response = true;})
.catch(() => { response = false;});
return value !== profile?.nickname && response;
}
}
// ... showing your error message
{
errors.nickname && errors.nickname.type=="validateNickname_" &&
<p className='error-form-msg'>
{"Your custom error message here ..."}
</p>
}

GraphQL Cannot return null for non-nullable field User.createdAt

#Mutation (() => UserResponse)
async register(
#Arg('options', () => UsernamePasswordInput ) options: UsernamePasswordInput,
#Ctx() {em, req}: MyContext
): Promise<UserResponse>{
const errors = validateRegister(options);
const hashedPassword = await argon2.hash(options.password)
let user;
try{
/* cast to this type, em as entity manager */
const result = await (em as EntityManager).createQueryBuilder(User).getKnexQuery().insert({
username: options.username,
password: hashedPassword,
email: options.email,
created_at: new Date(),
updated_at: new Date()
}).returning("*");
/* returns all fields back from the user */
user = result[0];
} catch(err: any ) {
if (err.code === '23505' || err.detail.includes("already exists")){
// duplicate name
/* returning errors object */
return {
errors: [{
field: 'username or email',
message: 'that username or email already exists'
}]
}
}
}
req.session.userId = user.id;
return {user};
}
import { Entity, PrimaryKey, Property } from "#mikro-orm/core";
import { Field, Int, ObjectType } from "type-graphql";
#ObjectType()
#Entity()
export class User {
#Field(() => Int)
#PrimaryKey()
id!: number;
#Field(() => String )
#Property({type: "date", default: "Now()", nullable: true})
createdAt = new Date();
#Field(() => String )
#Property({type: "date",onUpdate: () => new Date()})
updatedAt = new Date();
#Field(() => String)
#Property({type: 'text', unique: true})
username!: string;
#Field(() => String)
#Property({type: "text", unique: true})
email!: string;
/* this password has no graphql endpoint field */
#Property({type: 'text'})
password!: string;
}
const Register: React.FC<registerProps> = ({}) => {
const router = useRouter();
const [, register] = useRegisterMutation();
return (
<Wrapper variant = "small">
<Formik
initialValues = {{email: "", username: "", password: ""}}
onSubmit = {
async (values, {setErrors}) => {
/* this is a promise retruned therefore just return */
/* maps to the graphql mutation specified above */
const response = await register({options: values});
console.log("user: ", response)
/* code gen is detecting the types of this response from graphql */
/* check if the errors object has any errors */
if (response.data?.register.errors){
/* map then into a key:value pair for use in the formik set errors to nicely display the errors */
setErrors(toErrorMap(response.data.register.errors));
} else if(response.data?.register.user){
// worked
/* use next js hook to return to homepage */
router.push('/');
}
}}>
{/*
this is a render prop, we are taking values state and handleChange function from Formik , and passing our logic in
using the handles they gave us.
*/}
{({isSubmitting}) => (
<Form>
<InputField
name = "username"
placeholder = "username"
label = "Username"
/>
<Box mt = {4}>
<InputField
name = "email"
placeholder = "email"
label = "Email"
/>
</Box>
<Box mt = {4}>
<InputField
name = "password"
placeholder = "password"
label = "Password"
type = "password"
/>
</Box>
<Button
_hover = {{
backgroundColor: "purple",
color: "white",
}}
ml = "auto"
mr = "auto"
mt = {4}
display = "block"
type = "submit"
backgroundColor = "black"
/* this loads whenever we are submittting, a cool loading symbol */
isLoading = {isSubmitting}
>
register
</Button>
</Form>
)}
</Formik>
</Wrapper>
)
}
export default withUrqlClient(createUrqlClient, {ssr: true})(Register);
On my page when I pass an email / password / username to register
it returns this error from graphql
{
"errors": [
{
"message": "Cannot return null for non-nullable field User.createdAt.",
"locations": [
{
"line": 10,
"column": 7
}
],
"path": [
"register",
"user",
"createdAt"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"Error: Cannot return null for non-nullable field User.createdAt.",
" at completeValue (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:559:13)",
" at resolveField (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:472:19)",
" at executeFields (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:292:18)",
" at collectAndExecuteSubfields (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:748:10)",
" at completeObjectValue (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:738:10)",
" at completeValue (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:590:12)",
" at resolveField (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:472:19)",
" at executeFields (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:292:18)",
" at collectAndExecuteSubfields (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:748:10)",
" at completeObjectValue (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:738:10)",
" at completeValue (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:590:12)",
" at completeValue (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:556:21)",
" at D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:469:16",
" at processTicksAndRejections (node:internal/process/task_queues:96:5)",
" at async execute (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\apollo-server-core\\dist\\requestPipeline.js:205:20)",
" at async Object.processGraphQLRequest (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\apollo-server-core\\dist\\requestPipeline.js:139:28)"
]
}
}
}
],
"data": {
"register": {
"errors": null,
"user": null
}
}
}
This happens for no known reason , and I really am not sure what to even say at this point I have been confused on this issue for ages
......................... . .. . . . . . . ...................................................................................................................................................................
This usually always happens when a field, which is set up as non-nullable, is being returned as null from the server.
Please check the object which is returned from the server and verify if all the fields which you marked as per your set up, are being returned with some value.
One way to quickly check is to make all as { nullable: true } and see if that works, and then you can see which one is the offender.
Same tutorial. In src/resolvers/user.ts at the end of the try clause of the register mutation replace returning('*') with
.returning([
'*',
'created_at as createdAt',
'updated_at as updatedAt'
])
That shapes the server response to
result: [
{
id: 00,
username: 'jonh_d',
createdAt: 1985-10-29T03:17:23.000Z,
updatedAt: 1985-10-29T03:17:23.000Z
}
]

How to get Yup to perform more than one custom validation?

I'm working on a ReactJS project. I'm learning to use Yup for Validation with FormIk . The following code works fine:
const ValidationSchema = Yup.object().shape({
paymentCardName: Yup.string().required(s.validation.paymentCardName.required),
paymentCardNumber: Yup.string()
/*
.test(
"test-num",
"Requires 16 digits",
(value) => !isEmpty(value) && value.replace(/\s/g, "").length === 16
)
*/
.test(
"test-ctype",
"We do not accept this card type",
(value) => getCardType(value).length > 0
)
.required(),
But the moment I uncomment the test-num the developer tools complain about an uncaught promise:
How do I get Yup to give me a different error string based on the validation failure that I detect?
You can use the addMethod method to create two custom validation methods like this.
Yup.addMethod(Yup.string, "creditCardType", function (errorMessage) {
return this.test(`test-card-type`, errorMessage, function (value) {
const { path, createError } = this;
return (
getCardType(value).length > 0 ||
createError({ path, message: errorMessage })
);
});
});
Yup.addMethod(Yup.string, "creditCardLength", function (errorMessage) {
return this.test(`test-card-length`, errorMessage, function (value) {
const { path, createError } = this;
return (
(value && value.length === 16) ||
createError({ path, message: errorMessage })
);
});
});
const validationSchema = Yup.object().shape({
creditCard: Yup.string()
.creditCardType("We do not accept this card type")
.creditCardLength('Too short')
.required("Required"),
});

Antd Custom validator and validateFields are not working together as expected

I'm working on a form (Ant Design <Form>) where I want a custom validation of phone number (as it depends on country field and has some extra logic), and other fields validated by antd built-in features, so when user submits the form I want all fields to be validated with validateFields() passed as an array of field names showing validation errors as usual (red message underneath and an error message in console), but all I'm getting is just a warning message in the console.
Here's a minimal reproduction
Am I missing something about how validator function works?
make a if else check in your validatePhone function if value is not empty then run your code and if value is empty simply send a callback in else condition. like that
const validatePhone = (rule: any, value: any, callback: any) => {
if (value) {
const countryName = form.getFieldValue('country');
const country = CountryList.getCode(countryName);
const phoneNumber = parsePhoneNumberFromString(value, country as CountryCode);
console.log(form.getFieldValue('country'));
if (countryName && phoneNumber && phoneNumber.isValid()) {
updatePhonePrefix(prefix.concat(phoneNumber.countryCallingCode as string));
callback();
} else {
callback(`Phone number is not valid for ${countryName}`);
}
} else {
callback();
}
};
what the antd docs recommend is to use async or try-catch block
option 1:
const validatePhone = async (rule, value) => {
// check failing condition
throw new Error('Something wrong!');
}
option 2:
const validatePhone = (rule, value, callback) => {
try {
throw new Error('Something wrong!');
} catch (err) {
callback(err);
}
}
Here how I handled the error in antd form
import React, { useState } from 'react';
import { Form, Input } from 'antd';
function MyCustomForm(props: any) {
const [form] = Form.useForm();
const [validateFieldsName, setValidateFieldsName] = useState<string[]>([]);
const handleValidateFieldNames = (name: string) => {
const isFieldName = validateFieldsName.find(
(fieldName) => fieldName === name
);
if (isFieldName) return 'onChange';
return 'onBlur';
};
return (
<Form form={form} layout="vertical">
<Form.Item
name="contactNumber"
label="Contact Number"
validateTrigger={handleValidateFieldNames('contactNumber')}
rules={[
{
required: true,
message: 'Please enter contact number!',
},
{
validator(_, value) {
if (!value || value.length === 10) {
return Promise.resolve();
}
return Promise.reject('Please enter 10 digit Number!');
},
},
]}
>
<Input
type="number"
placeholder="Contact number"
onBlur={() =>
setValidateFieldsName([...validateFieldsName, 'contactNumber'])
}
/>
</Form.Item>
</Form>
);
}
export default MyCustomForm;

How can I validate an input's value on user keyboard entry?

How to validate form's input value on user typing or on change? I am trying to read state but it is kind of late/ not realtime.
I am thinking of using a class variable/ property and mutate it, but I am afraid that it will offend the React's principal.
Is there a proper way to create realtime form validation like this in React?
Validation is so widely used that we can find dozens of good ways to do that with react. I like to use the following:
Instead of just hold the value of your inputs on state, you could make a more complex object for each one. Let's begin defining a form with 2 inputs: name and age. The first step would be describe the form in state. Something like that:
state = {
form:{
name:{
value : '',
valid : true,
rules:{
minLength : 3
}
},
age:{
value : '',
valid : true,
rules:{
isNumber : true
}
}
}
}
There we have it! We now have 2 inputs that are valid on the initial render and have their own validation rules(isNumber, minLength). Now we need to write a function that validates the state on the fly. Let's write it then:
onChangeHandler = (key, value) =>{
this.setState(state =>({
...state,
form:{
...state.form,
[key]:{
...state.form[key],
value,
valid : validate(value, state.form[key].rules)
}
}
}))
}
Now we have a form described in state and a handler that updates the state onChange and validate the value of the input on each call. Now the only thing to do is write your validate() function and you are ready to go.
validate = (value, rules) => {
let valid = true
for (let key in rules) {
switch (key) {
case 'minLength':
valid = valid && minLengthValidator(value, rules[key])
break
case 'isNumber':
valid = valid && isNumberValidator(value)
break
default: break
}
}
return valid
}
Now the validators...
minLengthValidator = (value, rule) => (value.length >= rule)
isNumberValidator = value => !isNaN(parseFloat(value)) && isFinite(value)
Done! Now call your inputs like that:
render(){
const { form } = this.state
return(
<TextField value={form.name.value} onChange={e => this.onChangeHandler('name',e.target.value)} />
)
}
Every time that the input changes the validate function will be triggered, now you have real time form validation, is up to you apply the respectives styles according to the valid prop.
we have to define a validateProperty(input) and use it in our onChange method. here is a basic example how to implement it.first define your state. assuming that i have a form with username and password inputs.
state = { account: { username: "", password: "" }, errors: {} };
validateProperty = (input) => {
if (input.name === "username") {
if (input.value.trim() === "") return "username is required";
}
if ((input.name = "password")) {
if (input.value.trim() === "") return "password is required";
}
};
now we have to use it in on handleChange
e.currentTarget returns input field and i named it as input and did object destructuring
handleChange = ({ currentTarget: input }) => {
const errors = { ...this.state.errors };
const errorMessage = this.validateProperty(input);
if (errorMessage) errors[input.name] = errorMessage;
else delete errors[input.name];
const account = { ...this.state.account };
account[input.name] = input.value; //currentTarget returns input field
this.setState(() => ({ account, errors }));
};
for each input field I added "name" attribute. so errors[input.name] will be errors[e.currentTarget.name] if name="username" username field, if name="password" password field.

Resources