7

I have a form where if a person's shipping address is different from their Billing address they will select a radio button saying "no".

I need to be able to check what the value of this is, and if it is "no" check the form that unfolds. if it is "yes" I skip on to the next validations below. The "yes section is working, but the "no" section always returns invalidated. is there a way to nest validations in a conditional like this with this library?

Here is a screenshot of the form:

Test Form Image

Here is the validation code i have for this scenario:

    router.post('/testData2/', [
      body('billingSameAsShipping', "Please Fill All Fields Of Your Billing Address").custom((value) => {
        if (value === 'no') {
          [
            body('billingFirstName', "Please Enter Your First Name").isLength({
              min: 1
            }),
            body('billingLastName', "Please Enter Your Last Name").isLength({
              min: 1
            }),
            body('billingAddress1', "Please Enter Your Billing Address").isLength({
              min: 1
            }),
            body('billingZip', "Please Enter Your Billing ZipCode").isLength({
              min: 1
            }),
            body('billingCity', "Please Enter Your Billing City").isLength({
              min: 1
            }),
            body('billingState', "Please Enter Your Billing State").isLength({
              min: 1
            })
          ]
        } else {

          return Promise.resolve();
        }

      }),
    body('creditCardNumber', 'Please Enter A Vaild Credit Card Number').isCreditCard(),
    body('expmonth', 'Exp Month Empty').isLength({
      min: 1
    }),
    body('expyear', 'Exp Year Empty').isLength({
      min: 1
    }),
    body('CVV', 'CVV Empty').isLength({
      min: 3
    })
  ],
  (req, res) => {...

Here is the request object we are checking against

{
  billingSameAsShipping: 'no',
  billingFirstName: 'First',
  billingLastName: 'Last',
  billingAddress1: '450 Test Ave',
  billingZip: '12345',
  billingCity: 'San Diego',
  billingState: 'CA',
}

3 Answers 3

14

I got it, Express Validator has a conditional check built-in

I used oneOf([]) to check if the value of the radio buttons was "yes" or check that the billing address fields were filled in.

router.post('/testData2/', [
    oneOf([
      [
        body('billingFirstName', "Please Enter Your First Name").isLength({
          min: 1
        }),
        body('billingLastName', "Please Enter Your Last Name").isLength({
          min: 1
        }),
        body('billingAddress1', "Please Enter Your Billing Address").isLength({
          min: 1
        }),
        body('billingZip', "Please Enter Your Billing ZipCode").isLength({
          min: 1
        }),
        body('billingCity', "Please Enter Your Billing City").isLength({
          min: 1
        }),
        body('billingState', "Please Enter Your Billing State").isLength({
          min: 1
        })
      ],
      body('billingSameAsShipping').equals('yes')
    ], "Please Fill Out All Fields Of Your Billing Address"),
    body('creditCardNumber', 'Please Enter A Vaild Credit Card Number').isCreditCard(),
    body('expmonth', 'Exp Month Empty').isLength({
      min: 1
    }),
    body('expyear', 'Exp Year Empty').isLength({
      min: 1
    }),
    body('CVV', 'CVV Empty').isLength({
      min: 3
    })
  ],
  (req, res) => {...

That seems to be working correctly now.

Anyone have any other solutions to this one, or see anything wrong with my solution above?

Sign up to request clarification or add additional context in comments.

3 Comments

i'm using custom validators which helps me to easier parse the errors on client side body("billingFirstName") .custom((value, { req }) => { return req.body.billingSameAsShipping === 'yes' ? true : value }) .isLength({ min: 1 }) .withMessage("Please Enter Your First Name")
Hi Timothy, I hope you still around as I need help with oneO(), Your answer allowed me to check for the condition but I get an error that node_modules\express-validator\src\middlewares\check.js:19 next(e); In my endpoint I check the results like so : const ValidationResults = validationResult(req) console.log(" error :", ValidationResults.array())
error : [ { msg: Promise { <pending> }, param: '_error', nestedErrors: [ [Object], [Object] ] } ] I see that the promise is pending but I don't know how to fix that? How did you handle the promise in the endpoint?
7

You can also use this conditional which allows to do stuff like:

body('billingFirstName').if(body('billingSameAsShipping').equals('yes')).notEmpty(),

and the sort

Comments

1

There is an easier way to turn validators conditional. The condition is a boolean, so you can simply use the && logical operator to decide whether to add a validator to the array or not.

router.post('/testData2/', [

   req.body.billingSameAsShipping && body('billingFirstName')
      .isLength({ min: 1 }),

   req.body.billingSameAsShipping && body('billingLastName')
      .isLength({ min: 1 })

   ].filter(x => !!x)
)

The filter at the end is needed because if the condition evaluates to false, it will add false to the array. That would cause a runtime error, so it has to be removed. This is the most concise way to add validators conditionally.

2 Comments

This seems to be incorrect. I wonder how you are getting the context of req object for comparisons when its not a Middleware at all.
i wonder if inversing (puting a true in the array) would also need filtering. like [!req.body.billingSameAsShipping || body('billingFirstName').isLength({min: 1}), ...]

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.