2

I looked in the class-validators samples and docs but cant find the validation I need.

I have an array of object literals each with specific properties and values.

const comboItems = [{itemType: 'Entree'}, {itemType: 'Side'}, {itemType: 'Beverage'}];

I want to validate that comboItems is not empty and contains a min and max of 3 objects AND that there is an itemType === 'Entree' AND an itemType==='Side' AND an itemType==='Beverage'

This is the class I've created which isnt giving the correct validation:

import {validate, ArrayContains, ArrayNotEmpty, ArrayMinSize, ArrayMaxSize} from 'class-validator';
import { plainToClass } from 'class-transformer';

export class MealMenuItem {
    @ArrayContains([{itemType: 'Entree'}, {itemType: 'Side'}, {itemType: 'Beverage'}])
    @ArrayNotEmpty()
    @ArrayMinSize(3)
    @ArrayMaxSize(3)
    comboItems: any[];
}

const mealMenuItemData: any = {comboItems: [{itemType: 'Entree'}, {itemType: 'Side'}, {itemType: 'Beverage'}]};

const mealMenuItemDataClassInstance = plainToClass(MealMenuItem, mealMenuItemData as MealMenuItem)

validate(mealMenuItemDataClassInstance).then(errors => {
    if (errors.length > 0) 
        console.log('validation failed. errors: ', JSON.stringify(errors));
    else 
        console.log('validation succeed');
});

Thanks for any help!

1
  • This line is not a valid object => const mealMenuItemData: any = {comboItems: [{itemType: 'Entree', itemType: 'Side', itemType: 'Beverage'}]};you should change it as: const mealMenuItemData: any = {comboItems: [{itemType: 'Entree'}, {itemType: 'Side'}, {itemType: 'Beverage'}]}; Commented Nov 8, 2020 at 8:42

1 Answer 1

3

The ArrayContains decorator method just validate primitive types due to this line of class-validator module:

export function arrayContains(array: unknown, values: any[]): boolean {
  if (!(array instanceof Array)) return false;

  // Here return false for object items
  return values.every(value => array.indexOf(value) !== -1);
}

source code

So you can create a custom decorator for validation of your itemType field as bellow:

import {
    validate,
    ArrayMinSize,
    ArrayMaxSize,
    registerDecorator,
    ValidationOptions,
    ValidationArguments
} from 'class-validator';
import { plainToClass } from 'class-transformer';

function ContainSequenceOf(property: string, validationOptions?: ValidationOptions & { containThese: string[] }) {
    return function (object: Object, propertyName: string) {
        registerDecorator({
            name: 'containSequenceOf',
            target: object.constructor,
            propertyName: propertyName,
            constraints: [property],
            options: validationOptions,
            validator: {
                validate(value: any, args: ValidationArguments) {
                    const [relatedPropertyName] = args.constraints;
                    return value.every((item, i) => {
                        return item[relatedPropertyName] === validationOptions?.containThese[i]
                    })
                },
            },
        });
    };
}
export class MealMenuItem {
    @ContainSequenceOf('itemType', {
        message: "Is Not Valid!",
        containThese: ['Entree', 'Side', 'Beverage']
    })
    @ArrayMinSize(3)
    @ArrayMaxSize(3)
    comboItems: any[];
}

const mealMenuItemData: any = {comboItems: [{ itemType: 'Entree' }, { itemType: 'Side' }, { itemType: 'Beverage' }]};
const mealMenuItemDataClassInstance = plainToClass(MealMenuItem, mealMenuItemData as MealMenuItem)

validate(mealMenuItemDataClassInstance).then(errors => {
    if (errors.length > 0) 
        console.log('validation failed. errors: ', JSON.stringify(errors));
    else 
        console.log('validation succeed');
});
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the feedback. comboItems contains an array of objects. I also want to validate that each of the threes objects has a property "itemType" and that all the values you've listed are present. Is there a way to run some custom validation function on comboItems? For example: @ArrayContains(reducerValidate). reducerValidate((curr, acc) => {const validVals = ['Entree', 'Side', 'Beverage']; validValids.includes(curr['itemType'] && acc.push(curr['itemType']), []).length === 3
Sorry for the confusion. comboItems is an array of objects. I updated the code in my post to reflect this. How can each of these objects be validated?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.