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
$each does not work on non-objects #1139
Comments
Affecting me as well. I had to write a custom forEach to handle cases where items are not objects. For the record, the code below is my own helper, based on vuelidate's own. It can probably be optimised, but I needed something fast to be able to move over to the new version. /*
* https://github.com/vuelidate/vuelidate/blob/next/packages/validators/src/utils/forEach.js
*/
import { computed } from 'vue'
import { helpers } from '@vuelidate/validators'
export default function forEach (validators, forceSimple = false) {
return {
$validator (collection, ...others) {
return helpers.unwrap(collection).reduce(
(previous, collectionItem, index) => {
if (!forceSimple && typeof collectionItem === 'object') {
const collectionEntryResult = objectForEach(collectionItem, index, validators, others)
return {
$valid: previous.$valid && collectionEntryResult.$valid,
$data: previous.$data.concat(collectionEntryResult.$data),
$errors: previous.$errors.concat(collectionEntryResult.$errors)
}
}
const propertyResult = primitiveForEach(collectionItem, index, validators, others)
return {
$valid: previous.$valid && propertyResult.$valid,
$data: previous.$data.concat(propertyResult.$data),
$errors: previous.$errors.concat(propertyResult.$errors)
}
},
{ $valid: true, $data: [], $errors: [] }
)
},
$message: ({ $response }) => {
if (!$response) return []
return $response.$errors.map(context => {
if (context.$model) return context.$message
return Object.values(context)
.map(errors => (Array.isArray(errors) ? errors.map(e => e.$message) : errors.$message))
.reduce((a, b) => a.concat(b), [])
})
},
$params: computed(() => {
return Object.entries(validators).reduce((map, [key, value]) => {
map[key] = { ...value.$params }
return map
}, {})
})
}
}
function primitiveForEach ($model, index, validators, others) {
const innerValidators = validators || {}
return Object.entries(innerValidators).reduce(
(all, [validatorName, currentValidator]) => {
const validatorFunction = helpers.unwrapNormalizedValidator(currentValidator)
const $response = validatorFunction.call(this, $model, index, ...others)
const $valid = helpers.unwrapValidatorResponse($response)
all.$data[validatorName] = $response
all.$data.$invalid = !$valid || !!all.$data.$invalid
all.$data.$error = all.$data.$invalid
if (!$valid) {
let $message = currentValidator.$message || ''
const $params = currentValidator.$params || {}
if (typeof $message === 'function') {
$message = $message({
$pending: false,
$invalid: !$valid,
$params,
$model,
$response
})
}
all.$errors.push({
$message,
$params,
$response,
$model,
$pending: false,
$validator: validatorName
})
}
return {
$valid: all.$valid && $valid,
$data: all.$data,
$errors: all.$errors
}
},
{ $valid: true, $data: {}, $errors: [] }
)
}
function objectForEach (collectionItem, index, validators, others) {
return Object.entries(collectionItem).reduce(
(all, [property, $model]) => {
const innerValidators = validators[property] || {}
const propertyResult = Object.entries(innerValidators).reduce(
// eslint-disable-next-line no-shadow
(all, [validatorName, currentValidator]) => {
const validatorFunction = helpers.unwrapNormalizedValidator(currentValidator)
const $response = validatorFunction.call(this, $model, collectionItem, index, ...others)
const $valid = helpers.unwrapValidatorResponse($response)
all.$data[validatorName] = $response
all.$data.$invalid = !$valid || !!all.$data.$invalid
all.$data.$error = all.$data.$invalid
if (!$valid) {
let $message = currentValidator.$message || ''
const $params = currentValidator.$params || {}
if (typeof $message === 'function') {
$message = $message({
$pending: false,
$invalid: !$valid,
$params,
$model,
$response
})
}
all.$errors.push({
$property: property,
$message,
$params,
$response,
$model,
$pending: false,
$validator: validatorName
})
}
return {
$valid: all.$valid && $valid,
$data: all.$data,
$errors: all.$errors
}
},
{ $valid: true, $data: {}, $errors: [] }
)
all.$data[property] = propertyResult.$data
all.$errors[property] = propertyResult.$errors
return {
$valid: all.$valid && propertyResult.$valid,
$data: all.$data,
$errors: all.$errors
}
},
{ $valid: true, $data: {}, $errors: {} }
)
} |
I'm Confused about this answer, using a librairy is suppose to reduce and facilitate the work and absorb the complicated part of implementing functionality. I thinks i wil find another librairy making this kind of job. 🤔 |
Well, that code already exists, I just split it into two different parts, with small modifications. It is code already available in the library. |
I still have this problem, any helps? |
Like this? myEmails: {
isEmails: (vals) => vals.every(v => email.$validator(v)),
}, Documentation isn't clear on it |
@wadclapp |
If you need index when validating an array you could do this (as suggested in same issue linked above)? function validateEach(vals, validationObj) {
return vals.reduce((accVal, currVal, index) => {
accVal[index] = validationObj
return accVal
}, {})
} validations() {
return {
emails: validateEach(this.emails, {
email,
}),
}
} Output: {
...
"$invalid": false,
...
"emails": {
"0": {
...
"$invalid": false,
...
},
"1": { |
@tp27933 The replacement I have published above should work for your use case. You don't have to use it all the time, just when you need primitive support on forEach. It has been working well for my use case. |
@tp27933 Vuelidate has added a new helper called My code above is a modification of the original Just drop it in a file on your project, import it and use it in place of the original one. It should still support object as well. |
Describe the bug
$each
is responsible for evaluating collections. The new documentation says to use thehelpers.forEach
. The example in the docs uses an array of objects, but when I try to apply it to non-objects such as strings / numbers it does not work.Reproduction URL
The sandbox tries to evaluate the array of strings, but it doesn't pick up that anything is wrong.
Vuelidate 2 - Options API + Vue 3
To Reproduce
Steps to reproduce the behavior:
Expected behavior
The array should be invalid.
Additional context
Regarding the code sandbox. i know i can validate the email before it gets added.
The text was updated successfully, but these errors were encountered: