Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

question: <Advanced conditional validation> #2467

Closed
jbundziow opened this issue Apr 26, 2024 · 2 comments
Closed

question: <Advanced conditional validation> #2467

jbundziow opened this issue Apr 26, 2024 · 2 comments
Labels
type: question Questions about the usage of the library.

Comments

@jbundziow
Copy link

How to make more advanced conditional validation?

Look at the example:

export class CreateItemDto {
    @IsBoolean()
     price_show: boolean;
   
     @ValidateIf(o => o.price_show === true)
     @IsNumber()
     @Min(10)
     //TODO: if price_show is false, then price MUST BE NULL. How to achieve that?
     price: number | null;
}

The problem:
If price_show varriable is false all decorators are ignored and price can be any value.
Is there possibility to make something like 'else' statement to pass different decorators?

https://github.com/typestack/class-validator#conditional-validation

@jbundziow jbundziow added the type: question Questions about the usage of the library. label Apr 26, 2024
@BUONJG
Copy link

BUONJG commented May 6, 2024

Hello,

We had similar need for the application we develop and we created custom validators to manage those cases:

  • IsEmptyIf
  • IsOptionalIf
  • IsNotEmptyIf
  • ...

For the case you show it would be:

export class CreateItemDto {
    @IsBoolean()
     price_show: boolean;
   
     @IsOptionalIf<CreateItemDto>(o => o.price_show === false)
     @IsEmptyIf<CreateItemDto>(o => o.price_show === false)
     @IsNotEmptyIf<CreateItemDto>(o => o.price_show === true)
     @IsNumber()
     @Min(10)
     //TODO: if price_show is false, then price MUST BE NULL. How to achieve that?
     price: number | null;
}

It is a bit heavy but it works fine!

The custom validators

import { isEmpty, registerDecorator, ValidationArguments, ValidationOptions } from 'class-validator';

export function IsEmptyIf<T>(condition: (object: T) => boolean, validationOptions?: ValidationOptions) {
    return function (object: Object, propertyName: string) {
        registerDecorator({
            name: 'isEmpty',
            target: object.constructor,
            propertyName: propertyName,
            constraints: [condition],
            options: validationOptions,
            validator: {
                validate(value: any, args: ValidationArguments) {
                    const [relatedCondition] = args.constraints;
                    const cond = relatedCondition(args.object);

                    if (cond && Array.isArray(value) && validationOptions && validationOptions.each)
                        return value.every(v => isEmpty(v));
                     else if (cond)
                        return isEmpty(value);

                    return true;
                }
            }
        });
    };
}
// eslint-disable-next-line no-restricted-imports
import { isNotEmpty, registerDecorator, ValidationArguments, ValidationOptions } from 'class-validator';

export function IsNotEmptyIf<T>(condition: (object: T) => boolean, validationOptions?: ValidationOptions) {
    return function (object: Object, propertyName: string) {
        registerDecorator({
            name: 'isNotEmpty',
            target: object.constructor,
            propertyName: propertyName,
            constraints: [condition],
            options: validationOptions,
            validator: {
                validate(value: any, args: ValidationArguments) {
                    const [relatedCondition] = args.constraints;
                    const cond = relatedCondition(args.object);
                    if (cond)
                        return isNotEmpty(value);

                    return true;
                }
            }
        });
    };
}
import { ValidateIf, ValidationOptions } from 'class-validator';

/**
 * Checks if value is missing and if so, ignores all validators.
 **/
export function IsOptionalIf<T>(condition: (object: T, value: any) => boolean, validationOptions?: ValidationOptions) {
    return ValidateIf((object: T, value: any) => {
        const valueIsNil = (value === '' || value === null || value === undefined);
        return !valueIsNil || !condition(object, value);
    }, validationOptions);
}

If you find a better way, I would be interested. Indeed we would have loved to have a single validator to merge the 3 validator as we often use them together...

@jbundziow
Copy link
Author

Thanks for your reply BUONJG!

Someone helped me on stackoverflow a couple days ago.
I think that for my specific case it's the simplest method.

Anyway, validating more complex data is strange in this library...

@jbundziow jbundziow reopened this May 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: question Questions about the usage of the library.
Development

No branches or pull requests

2 participants