Skip to content
This repository was archived by the owner on Apr 9, 2025. It is now read-only.

Feature/1 gain consistency on the validation strategy #6

Merged
merged 6 commits into from
Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/FormHandler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import useFormHandler from './useFormHandler';
import { FormHandlerParams, FormHandlerReturn } from './types';
import { FormHandlerParams, FormHandlerReturn } from './types/formHandler';
import { defineComponent, PropType } from 'vue';

export default defineComponent({
Expand All @@ -8,15 +8,15 @@ export default defineComponent({
initialValues: Object as PropType<FormHandlerParams['initialValues']>,
interceptor: Function as PropType<FormHandlerParams['interceptor']>,
validate: Function as PropType<FormHandlerParams['validate']>,
options: Object as PropType<FormHandlerParams['options']>
validationMode: Object as PropType<FormHandlerParams['validationMode']>
},
setup: (props, {slots}) => {
const {...formHandler} = useFormHandler({
setup: (props, { slots }) => {
const { ...formHandler } = useFormHandler({
initialValues: props.initialValues,
interceptor: props.interceptor,
validate: props.validate,
options: props.options
validationMode: props.validationMode
})
return () => slots.default && slots.default({...formHandler} as FormHandlerReturn)
return () => slots.default && slots.default({ ...formHandler } as FormHandlerReturn)
}
})
2 changes: 1 addition & 1 deletion src/constants.ts → src/core/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BaseControlEmits } from "./types"
import { BaseControlEmits } from "../types"

export const DEFAULT_FIELD_VALUE = null;

Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { default as FormHandler } from './FormHandler'
export { default as useFormHandler } from './useFormHandler'

export * from './constants'
export * from './core/constants'
export * from './types'
2 changes: 1 addition & 1 deletion src/logic/getDefaultFieldValue.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isCheckboxInput } from "../utils"
import { DEFAULT_FIELD_VALUE } from "../constants"
import { DEFAULT_FIELD_VALUE } from "../core/constants"

export default (el: any) => {
if (!el) {
Expand Down
2 changes: 1 addition & 1 deletion src/logic/getNativeFieldValue.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DEFAULT_FIELD_VALUE } from './../constants';
import { DEFAULT_FIELD_VALUE } from '../core/constants';
import { isCheckboxInput, isMultipleSelect, isRadioInput } from "../utils"

export default (el: any) => {
Expand Down
6 changes: 5 additions & 1 deletion src/logic/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
export { default as getNativeFieldValue } from './getNativeFieldValue'
export { default as getDefaultFieldValue } from './getNativeFieldValue'
export { default as getDefaultFieldValue } from './getDefaultFieldValue'
export { default as validateField } from './validateField'
export { default as validateForm } from './validateForm'
export { default as refFn } from './refFn'
export { default as transformValidations } from './transformValidations'
50 changes: 50 additions & 0 deletions src/logic/refFn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Refs, FieldReference } from "../types";
import { isCheckboxInput, isMultipleSelect, isNativeControl, isRadioInput } from "../utils"

export default (name: string, _refs: Refs, values: any) => (fieldRef: any) => {
if (!fieldRef) {
delete _refs[name]
return
}
if (!fieldRef.nodeName || !isNativeControl(fieldRef)) {
//TODO: Correctly type this in order to expect a fixed data structure
_refs[name].ref = {
type: 'custom'
}
return
}
const isFirstRegister = _refs[name] && (!_refs[name].ref
|| (Array.isArray(_refs[name].ref)
&& isRadioInput(fieldRef)
&& (!(_refs[name].ref as FieldReference[])
.some((option: any) => option.value === fieldRef.value))))
if (isFirstRegister) {
_refs[name].ref = isRadioInput(fieldRef)
? [...(_refs[name].ref as FieldReference[] || [])
, fieldRef]
: fieldRef
}
if (isRadioInput(fieldRef)) {
if (isFirstRegister && fieldRef.checked) {
values[name] = fieldRef.value
return
}
fieldRef.checked = (values[name] === fieldRef.value)
return
}
if (isCheckboxInput(fieldRef)) {
if (isFirstRegister) {
values[name] = !!fieldRef.checked
return
}
fieldRef.checked = !!values[name]
return
}
if (isMultipleSelect(fieldRef)) {
[...fieldRef.options].forEach((option: any, index) => {
fieldRef[index].selected = !!values[name]?.includes(option.value)
})
return
}
fieldRef.value = values[name]
};
38 changes: 38 additions & 0 deletions src/logic/transformValidations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ValidationsConfiguration, ValidationWithMessage, Validations } from "../types";
import { max, maxLength, min, minLength, pattern, required } from "../utils";

export default (validations: ValidationsConfiguration = {}): Validations => {
const transformed: Validations = {};
if (!!validations.required) {
transformed.required = typeof validations.required === 'string'
? required(validations.required)
: required();
}
if (validations.min) {
transformed.min = typeof validations.min === 'number'
? min(validations.min)
: min(validations.min.value as number, validations.min.message);
}
if (validations.max) {
transformed.max = typeof validations.max === 'number'
? max(validations.max)
: max(validations.max.value as number, validations.max.message);
}
if (validations.minLength) {
transformed.minLength = typeof validations.minLength === 'number'
? minLength(validations.minLength)
: minLength(validations.minLength.value as number, validations.minLength.message);
}
if (validations.maxLength) {
transformed.maxLength = typeof validations.maxLength === 'number'
? maxLength(validations.maxLength)
: maxLength(validations.maxLength.value as number, validations.maxLength.message);
}
if (validations.pattern) {
transformed.pattern = !(validations.pattern as ValidationWithMessage)?.value
? pattern(validations.pattern as RegExp)
: pattern((validations.pattern as ValidationWithMessage).value as RegExp, (validations.pattern as ValidationWithMessage).message as string);
}
return transformed;
}

20 changes: 20 additions & 0 deletions src/logic/validateField.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ValidateFieldParams } from './../types/logic';

export default async ({ name, values, formState, _refs }: ValidateFieldParams): Promise<void> => {
if (!Object.keys(_refs[name]._validations).length) {
return
}
if (_refs[name]._disabled) {
return
}
for (const [validationName, validation] of Object.entries(_refs[name]._validations)) {
const result = await validation(values[name])
formState.errors[name] = {
...(result !== true && { [validationName]: result })
}
if (result !== true) {
break;
}
delete formState.errors[name]
}
}
8 changes: 8 additions & 0 deletions src/logic/validateForm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ValidateFormParams } from './../types/logic';
import validateField from "./validateField"

export default async (params: ValidateFormParams) => {
for (const name of Object.keys(params.values)) {
await validateField({ ...params, name })
}
}
79 changes: 27 additions & 52 deletions src/playground/App.vue
Original file line number Diff line number Diff line change
@@ -1,61 +1,36 @@
<template>
<h2> VSSTS </h2>
<input v-bind="register('hello')"><br>
<input type="number" v-bind="register('number')"><br>
<input type="radio" value="female" name="test"> Female<br>
<input type="radio" value="other" name="test"> Other<br>
<br>
<input type="radio" value="male" v-bind="register('gender')"> Male<br>
<input type="radio" value="female" v-bind="register('gender')"> Female<br>
<input type="radio" value="other" v-bind="register('gender')"> Other<br>
<br>
<input type="checkbox" v-bind="register('checkbox')"> Other <br>
<br>
<select v-bind="register('select1')" style="min-width:300px">
<option value="">--- Please select option ---</option>
<option value="dog">Dog</option>
<option value="cat">Cat</option>
<option value="hamster">Hamster</option>
<option value="parrot">Parrot</option>
<option value="spider">Spider</option>
<option value="goldfish">Goldfish</option>
</select><br>
<br>
<select v-bind="register('select')" style="min-width:300px" multiple>
<option value="dog">Dog</option>
<option value="cat">Cat</option>
<option value="hamster">Hamster</option>
<option value="parrot">Parrot</option>
<option value="spider">Spider</option>
<option value="goldfish">Goldfish</option>
</select><br>
<br>
<textarea v-bind="register('textArea')"></textarea>
<pre>{{ values }}</pre>
<pre>{{ formState }}</pre>
<pre>{{ modifiedValues() }}</pre>
<template v-if="!!dynamic">
<input v-bind="register('dynamic')"> <br>
</template> <br>


<button @click="setValue('gender', 'female')">Set radio to female</button>
<button @click="setValue('select', ['dog', 'cat'])">Set select to dog & cat</button>
<button @click="resetField('checkbox')">Reset checkbox</button>
<button @click="dynamic = true">Add dynamic field</button>
<form @submit.prevent="() => handleSubmit(submitForm)">
<h2> VSSTS </h2>
<input v-bind="register('hello', {
required: 'Something is required',
pattern: /^[A-Za-z]+$/i
})"><br>
<input type="number" v-bind="register('hello2', {
min: 5,
max: 10
})"><br>
<input v-bind="register('hello3', {
minLength: {
value: 5,
message: 'Min length is 5'
},
maxLength: 20,
disabled: values.hello2 > 5
})"><br>
<br>
<button>Submit</button>
<pre>{{ values }}</pre>
<pre>{{ formState }}</pre>
</form>
</template>

<script setup lang="ts">
import useFormHandler from '../useFormHandler';
import { ref } from 'vue'
const dynamic = ref(false)

const { register, values, formState, resetField, setValue, modifiedValues } = useFormHandler({
initialValues: {
gender: 'male'
}
});

const { register, handleSubmit, values, formState } = useFormHandler({});
const submitForm = (values: any) => {
console.log(values)
}
</script>

<style>
Expand Down
40 changes: 40 additions & 0 deletions src/types/baseControl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/** Props for a base control */
export interface BaseControlProps {
/** Name of the control */
name: string,

/** Current errors of the control */
errors: string[]

/** Value binding for native inputs */
ref: any,

/** Value binding for custom inputs */
modelValue: any,

/** Handler binding for custom inputs */
'onUpdate:modelValue': (value: any) => void,

/** Disabled state of the field*/
disabled?: boolean

/** Current dirty state of the control */
isDirty?: boolean

/** Current touched state of the control */
isTouched?: boolean

/** Handler binding for native inputs */
onChange?: (el: any) => void,

/** Blur handler */
onBlur?: () => void,

/** Clear handler */
onClear?: () => void,
}

/** Each emit the handler could be expecting */
export type EmitOption = 'update:modelValue' | 'blur' | 'clear'
/** Emit collection for a base control */
export type BaseControlEmits = EmitOption[]
Loading