yup validation on multiple values - formik

I want to validate my form using yup in formik. Suppose I have 4 fields A, B, C, D and they are all strings. How should I write the validation schema if I want to have at least one of the fields is not empty, then that's a valid form? Thanks in advance!

When using Yup if all normal features fail you, you can use the .test feature, documented here - https://github.com/jquense/yup#mixedtestname-string-message-string--function-test-function-schema
mixed.test(name: string, message: string | function, test: function): Schema
Adds a test function to the validation chain. Tests are run after any object is cast. Many types have some tests built in, but you can create custom ones easily. In order to allow asynchronous custom validations all (or no) tests are run asynchronously. A consequence of this is that test execution order cannot be guaranteed.
For your implementation you will want to write a "test" for each of your 4 fields to make sure one of the 4 are not null.
field1: yup
.string()
.test(
'oneOfRequired',
'One of Field1, Field2, Field3 or Field4 must be entered',
function(item) {
return (this.parent.field1 || this.parent.field2 || this.parent.field3 || this.parent.field4)
}
),
field2: yup
.string()
.test(
'oneOfRequired',
'One of Field1, Field2, Field3 or Field4 must be entered',
function(item) {
return (this.parent.field1 || this.parent.field2 || this.parent.field3 || this.parent.field4)
}
),
etc...
Please note in this case I have not used an arrow function. This is because to use the 'this' context you must use this syntax, this is mentioned in the Yup documentation.

There's another possibility if you don't want to add the validation to every field but rather have a "global" error handler for these things.
You'd do something like this:
const schema = yup.object().shape({
field1: yup.string().required(),
field2: yup.string().required(),
field3: yup.string().required(),
field4: yup.string().required(),
}).test('yourTestCondition', function (value) {
// your global test code...
})

There is a solution for what you're searching. Instead of writing a test for each of the elements, you could just write one for the parent. Simulating a Global error.
yup.object({
field1: yup.string(),
field2: yup.string(),
field3: yup.string(),
field4: yup.string(),
})
.test('global-ok',
'you do not fulfill the requirements',
function (value) {
return CONDITION OVER THE CHILDREN;
})
For example, if you don't want to write the error for a series of required elements and just give one type of global error. You could:
yup.object({
username: yup.string().required(),
password: yup.string().required(),
email: yup.string().required().test(verify_email),
})
.test('global-ok',
'The data is not correct',
function (value) {
return username && password && email;
})

This is an example to put things in context
Consider Student name is required only if he/she is from USA and is above 10 years, for this case this is how we can write yup configuration.
Note: multiple fields are used for validation
const formSchema = yup.object({
age: yup.number(),
country: yup.string(),
studentName: yup.string().when(['age', 'country'], {
is: (age: string, country: string) => {
if (age > 10) {
return true // validate and go to then function
}
if (country === 'USA') {
return true // validate and go to then function
} else return false // for all other cases studentName is not required
},
then: (schema) =>
schema
.required("Student name is required")
})
})

email: Yup.string()
.when([‘, 'showEmail’, ’anotherField’], {
is: (showEmail, anotherField) => {
return (showEmail && anotherField);
},
then: Yup.string().required('Must enter email address')
}),
Multiple fields can also be used for validation.
The easiest way to handle multiple params

If you prefer ES6 and want to use arrow functions, a modified version of Jamie answer answer would be like this:
field1: yup
.string()
.test(
'oneOfRequired',
'One of Field1, Field2, Field3 or Field4 must be entered',
(item,testContext)=>{
return (testContext.parent.field1 || testContext.parent.field2 || testContext.parent.field3 || testContext.parent.field4)
}
),
field2: yup
.string()
.test(
'oneOfRequired',
'One of Field1, Field2, Field3 or Field4 must be entered',
(item,testContext)=> {
return (testContext.parent.field1 || testContext.parent.field2 || testContext.parent.field3 || testContext.parent.field4)
}
),
For details, official docs.

lazy(value => {
switch (typeof value) {
case 'array':
return array().of(string()).nullable();
case 'string':
return string().nullable();
default:
return array().of(string()).nullable();
}
}),

Related

Apollo Client 3, some way to filter each payload of mutations

I wonder if there is a way (plugin, middleware, etc) to apply some function to filter the string that is used on each mutation.
For instance:
// In my schema
input Comment {
status: "SENT",
comment: String
}
mutation updateStatus($id: String, $input: Comment!) {
updateStatus(id: $id, input: $input)
}
// and I call the mutation from React
useMutation(UPDATE_STATUS, { options });
But I have many similar mutations where I use some "input", so I wonder if it's possible to filter each used input with a simple function like:
// This will take each value of the input object and replace the strings that match with "some char"
const clearMutationPayload = (input) => Object.values(input).map(val => val.replace('some char', '');
Any idea?

How to implement conditional Validation in Nested DTOs - NestJS?

I am working in NestJS Project, there is one situation where I need to implement conditional Validation.
So my Payload looks like this:
{
user_id: "123"
user_type: "M" //Value Can be M or F
inner_details: {
name: {
firstname:"akshay",
lastname:"nikte"
},
email: "ak#test.com"
},
user_co: "Tesla"
}
So in above payload(body) there are some validation requirement as below:
If user_type = M then Firstname and Lastname both cannot be Empty
If user_type = F then Firstname cannnot be Empty but Lastname can be Empty
For this I created 3 DTO classes:
Filename: create-user.dto.ts
export Class CreateUserDTO {
#IsNotEmpty()
#ApiProperty({
required:true
})
user_id: string
#IsNotEmpty()
#ApiProperty({
required:true
})
user_type: string
#ValidateNested({each:true})
#Type(()=>InnerDetailsDTO)
inner_details: InnerDetailsDTO
#IsNotEmpty()
user_co: string
}
filname: inner-details.dto.ts
export class InnerDetailsDTO {
#ValidateNested({each: true})
#Type: NameDTO
name: NameDTO
#IsNotEmpty
#ApiProperty({
required: true
})
email: string
}
filename name.dto.ts
export class NameDTO {
#IsNotEmpty()
#ApiProperty({required: true})
firstname: string
#Validateif(check if UserType==="M") // How to access UserType from CreateUser DTO
#IsNotEmprty()
lastname: string
}
I have have 3 different DTOs in 3 different Files, how to use attribute from 1 DTO in another DTO ?
In my NameDTO I want apply conditional validation, when user_type==M then validate both Firstname and Lastname but when user_type==F Then only validate Firstname and not Lastname
I recommend yup. There you can also make conditional validation. But therefore you have to setup yup as a Validation Pipeline in Nestjs as well. See this npm module here. But this package is not really popular, so I would implement it myself (I actually did that and it works pretty well). This video explains it really good.

How can I specify a Yup error on a validating field's parent schema?

I'm trying to set up a validation schema to use with Formik. I have a combobox that has items of shape { id: number; value: string; } and pulls the value out to display to the user, while submitting the whole item to Formik/Yup. However, in production, I won't know the shape of my items ahead of time; this is just the shape I've chosen for demoing.
const items = [
{ id: 1, value: 'foo' },
{ id: 2, value: 'bar' },
];
const [ field, meta, helpers ] = useField('value');
return (
<ComboBox
{...field}
invalid={meta.touched && !!meta.errors}
invalidText={meta.errors}
items={items}
itemToString={i => i?.value ?? ''}
onChange={data => helpers.setValue(data.selectedItem)}
selectedItem={field.value}
/>
);
I want to make only id = 1 to be valid.
const validationSchema = Yup.object({
value: Yup.object({
id: Yup.number().oneOf([1], "You've selected an invalid option."),
value: Yup.string(),
})
.required('You have not selected an option.')
.nullable(),
});
However, when this is in the error state, meta.errors is set to { id: "You've selected an invalid option." }. invalidText expects a ReactChild so when it receives this object, React throws an error.
I assume the solution, then, is to move .oneOf() to outside of the inner Yup.object(). I don't know how to specify the valid values, however, and neither the documentation nor a quick search of SO helped. Thoughts?

Yup validation with one field but two possible options (OR)

I am trying to create an input that takes a string that will be used as the href value for a tag. The href can be a url OR an email (for mailto:).
It works if I just check for email, or if I just check for URL. However, I want to check for one or the other. I am looking through yup documentation but I can't find a way to do an OR.
I noticed that there is a when to test for another field but I'm not checking if another field is true or not, or use test but I also can't seem to get it to work.
const vSchema = yup.object().shape({
text: yup.string().required(),
href: yup
.string()
.email('Link must be a URL or email')
.url('Link must be a URL or email')
.required('Link is a required field'),
});
test this
yup.addMethod(yup.string, "or", function(schemas, msg) {
return this.test({
name: "or",
message: "Please enter valid url or email." || msg,
test: value => {
if (Array.isArray(schemas) && schemas.length > 1) {
const resee = schemas.map(schema => schema.isValidSync(value));
return resee.some(res => res);
} else {
throw new TypeError("Schemas is not correct array schema");
}
},
exclusive: false
});
});

apollo angular how to use a nullable but required argument in mutations?

I would like to use a typescript interface for an update mutation:
export interface UpdateDescription {
title: string;
publishedFrom: date | null;
}
So if null is passed as value of publishedFrom, the original date should be removed on the server. If the key would be optional and publishedFrom is not provided, but in the model there already is set a value, it would be removed as well. This is not an option! Is there is possibility to write a mutation like:
mutation UpdateExample($id: ID!, $title: String!, $publishedFrom: ISO8601DateTime!) {
updateExample(input: {id: $id, title: $title, publishedFrom: $publishedFrom}) {
errors
}
}
and make publishedFrom required but nullable?

Resources