diff --git a/package.json b/package.json index 19038c3..82c7778 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@wgr-sa/nuxt-form", "description": "Form builder for Nuxt", - "version": "0.8.9", + "version": "0.9.0", "repository": "https://github.com/WGR-SA/nuxt-form.git", "author": "jeanvier", "license": "MIT", diff --git a/playground/app.vue b/playground/app.vue index 820a5f3..b3cecad 100644 --- a/playground/app.vue +++ b/playground/app.vue @@ -3,6 +3,9 @@ // watchEffect(() => { // if testForm.value.isReady() // testForm.value.submit() + +import { max } from 'class-validator'; + // }) @@ -12,15 +15,15 @@ ref="testForm" action="https://httpbin.org/post" > - - - - - - + /> --> + + + + Submit diff --git a/src/runtime/composables/validator.ts b/src/runtime/composables/validator.ts index a1ceac6..4c08884 100644 --- a/src/runtime/composables/validator.ts +++ b/src/runtime/composables/validator.ts @@ -3,43 +3,71 @@ import * as validators from 'class-validator' import { Form } from '#imports' export const useFormValidator = () => { + const validatorTypeMap = new Map([ + ['number', ['isNumber', 'min', 'max', 'isInt', 'isFloat', 'isDivisibleBy', 'isPositive', 'isNegative', 'isLessThan', 'isGreaterThan', 'isLatitude', 'isLongitude', 'isPort']], + ['date', ['isDate', 'minDate', 'maxDate', 'isBefore', 'isAfter']], + ['boolean', ['isBoolean']], + ['array', ['arrayContains', 'arrayNotContains', 'arrayNotEmpty', 'arrayUnique']] + ]) - const getFieldErrors = (form: Form, field: string): string[] => { - - const errors = ref([]) + const getExpectedType = (validatorName: string): string => { + for (const [type, validatorList] of validatorTypeMap) { + if (validatorList.includes(validatorName)) return type + } + return 'string' + } - if (!form.validator.rules[field]) { - return [] - } + const convertValue = (value: string, expectedType: string): any => { + switch (expectedType) { + case 'number': return Number(value) + case 'boolean': return value.toLowerCase() === 'true' + case 'date': return new Date(value) + case 'array': return tryParseJSON(value) ?? value.split(',').map(item => item.trim()) + default: return value + } + } - form.validator.rules[field].forEach((rule: any) => { - - const validator = validators[rule.$params.type as keyof typeof validators] + const tryParseJSON = (value: string): any => { + try { + return JSON.parse(value) + } catch { + return 2 + } + } + const getFieldErrors = (form: Form, field: string): string[] => { + if (!form.validator.rules[field]) return [] - const type_rules = [ 'isEmail', 'isNumber' ] - // validate type only if field is not empty - if (type_rules.includes(rule.$params.type) && (form.data.state[field].length === 0 || form.data.state[field] === null)) { - return + const errors: string[] = [] + const fieldValue = form.data.state[field] + + for (const rule of form.validator.rules[field]) { + const validatorName = rule.$params.type as keyof typeof validators + const validator = validators[validatorName] + + if (!validator) { + console.error(`Validator ${validatorName} not found`) + continue } - if (field in form.data.state) { - // @ts-ignore TODO: import only validation functions - const result = validator(form.data.state[field].toString(), rule.$params.options) + if (['isEmail', 'isNumber'].includes(validatorName) && !fieldValue) continue - if (!result && (form.data.state[field].length > 0 && form.data.state[field] !== 'false' || ['error', 'validate'].includes(form.state.status))) { - errors.value.push(rule.custom_message ?? rule.$message) + if (fieldValue !== undefined) { + const expectedType = getExpectedType(validatorName) + const convertedValue = convertValue(fieldValue, expectedType) + + if (!validator(convertedValue, ...(rule.$params.options || [])) && + (fieldValue.length > 0 && fieldValue !== 'false' || ['error', 'validate'].includes(form.state.status))) { + errors.push(rule.custom_message ?? rule.$message) } } - - }) + } - return errors.value + return errors } - const validateFields = async (form: Form): Promise => { - return !Object.keys(form.validator.rules).some((field: string) => getFieldErrors(form, field).length > 0) - } + const validateFields = (form: Form): boolean => + Object.keys(form.validator.rules).every((field: string) => getFieldErrors(form, field).length === 0) return { getFieldErrors, diff --git a/src/runtime/messages/validators/en.ts b/src/runtime/messages/validators/en.ts index 288b2bb..69ca470 100644 --- a/src/runtime/messages/validators/en.ts +++ b/src/runtime/messages/validators/en.ts @@ -2,6 +2,8 @@ export const en: {[key: string]: string} = { equals: 'This field must be equal to {0}', contains: 'This field must contain {0}', matches: 'This field must match the format {0}', + max: 'This field must be less than or equal to {0}', + min: 'This field must be greater than or equal to {0}', isEmail: 'This field must be a valid email address', isURL: 'This field must be a valid URL', isMACAddress: 'This field must be a valid MAC address', diff --git a/src/runtime/messages/validators/fr.ts b/src/runtime/messages/validators/fr.ts index 5484459..8c37c60 100644 --- a/src/runtime/messages/validators/fr.ts +++ b/src/runtime/messages/validators/fr.ts @@ -2,6 +2,8 @@ export const fr: {[key: string]: string} = { equals: 'Ce champ doit être égal à {0}', contains: 'Ce champ doit contenir {0}', matches: 'Ce champ doit correspondre au format {0}', + min: 'Ce champ doit être supérieur ou égal à {0}', + max: 'Ce champ doit être inférieur ou égal à {0}', isEmail: 'Ce champ doit être une adresse email valide', isURL: 'Ce champ doit être une URL valide', isMACAddress: 'Ce champ doit être une adresse MAC valide', diff --git a/src/runtime/utils/validators/validator.ts b/src/runtime/utils/validators/validator.ts index 620a855..c322f4a 100644 --- a/src/runtime/utils/validators/validator.ts +++ b/src/runtime/utils/validators/validator.ts @@ -9,7 +9,7 @@ export class FormValidator { return { $params: { type: r }, $message: r } } const rule = Object.keys(r)[0] - return { $params: { type: rule, options: [...r[rule]] }, $message: r, custom_message: r.message } + return { $params: { type: rule, options: [...r[rule]] }, $message: rule, custom_message: r.message } }) this.updateValidatorMessages(options.messages)