Skip to content
This repository was archived by the owner on Jun 27, 2023. It is now read-only.

Commit ec98131

Browse files
committed
feat(validations): improved validation system
1 parent 484ad01 commit ec98131

File tree

12 files changed

+218
-88
lines changed

12 files changed

+218
-88
lines changed

dev/typescript/App.vue

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,6 @@
6565
import { mockAsync } from '@/core/utils/helpers';
6666
import { computed, defineComponent, onMounted, reactive, ref } from 'vue';
6767
import {
68-
FormValidation,
69-
email,
70-
pattern,
7168
TextField,
7269
SelectField,
7370
EmailField,
@@ -77,6 +74,11 @@ import {
7774
RadioField,
7875
CustomField,
7976
ColorField,
77+
Validator,
78+
FormValidator,
79+
required,
80+
email,
81+
pattern,
8082
} from '../../src';
8183
/* } from '../../dist/as-dynamic-forms.esm'; */
8284
export default defineComponent({
@@ -85,18 +87,18 @@ export default defineComponent({
8587
const title = ref('Vue Dynamic Forms');
8688
const formValues = reactive({});
8789
let consoleOptions = ref();
88-
const emailValidator: FormValidation = {
90+
const emailValidator: FormValidator = {
8991
validator: email,
9092
text: 'Email format is incorrect',
9193
};
9294
93-
const passwordValidator: FormValidation = {
95+
const passwordValidator: FormValidator = Validator({
9496
validator: pattern(
9597
'^(?=.*[a-z])(?=.*[A-Z])(?=.*)(?=.*[#$^+=!*()@%&]).{8,10}$',
9698
),
9799
text:
98100
'Password must contain at least 1 Uppercase, 1 Lowercase, 1 number, 1 special character and min 8 characters max 10',
99-
};
101+
});
100102
101103
const form = computed(() => ({
102104
id: 'example-form',
@@ -118,7 +120,9 @@ export default defineComponent({
118120
fields: {
119121
name: TextField({
120122
label: 'Name',
121-
required: true,
123+
validations: [
124+
Validator({ validator: required, text: 'This field is required' }),
125+
],
122126
}),
123127
email: EmailField({
124128
label: 'Email',

src/components/dynamic-form/DynamicForm.vue

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
:submited="submited"
1515
@change="valueChange"
1616
@blur="onBlur"
17+
@validate="onValidate"
1718
>
1819
<template v-slot:customField="props">
1920
<div
@@ -50,7 +51,14 @@ import { diff } from 'deep-object-diff';
5051
5152
import DynamicInput from '../dynamic-input/DynamicInput.vue';
5253
53-
import { DynamicForm, FieldTypes, FormControl, InputType } from '@/core/models';
54+
import {
55+
DynamicForm,
56+
FieldTypes,
57+
FormControl,
58+
InputType,
59+
ValidationEvent,
60+
InputEvent,
61+
} from '@/core/models';
5462
import { dynamicFormsSymbol } from '@/useApi';
5563
import { deepClone, hasValue, removeEmpty } from '@/core/utils/helpers';
5664
@@ -194,20 +202,27 @@ export default defineComponent({
194202
if (updatedCtrl) {
195203
updatedCtrl.value = event.value as string;
196204
updatedCtrl.dirty = true;
197-
validateControl(updatedCtrl);
198205
}
199206
ctx.emit('change', formValues.value);
200207
}
201208
}
202209
203-
function onBlur(control: FormControl<InputType>) {
204-
const updatedCtrl = findControlByName(control.name);
210+
function onBlur({ name }: InputEvent) {
211+
const updatedCtrl = findControlByName(name);
205212
if (updatedCtrl) {
206213
updatedCtrl.touched = true;
207214
}
208215
}
209216
210-
function validateControl(control: FormControl<InputType>) {
217+
function onValidate({ name, errors, valid }: ValidationEvent) {
218+
const updatedCtrl = findControlByName(name);
219+
if (updatedCtrl) {
220+
updatedCtrl.errors = errors;
221+
updatedCtrl.valid = valid;
222+
}
223+
}
224+
225+
/* function validateControl(control: FormControl<InputType>) {
211226
if (control.validations) {
212227
const validation = control.validations.reduce((prev, curr) => {
213228
const val =
@@ -233,7 +248,7 @@ export default defineComponent({
233248
control.errors = validation;
234249
control.valid = Object.keys(validation).length === 0;
235250
}
236-
}
251+
} */
237252
238253
function detectChanges(fields) {
239254
const changes = diff(cache, deepClone(fields));
@@ -288,6 +303,7 @@ export default defineComponent({
288303
submited,
289304
formattedOptions,
290305
onBlur,
306+
onValidate,
291307
};
292308
},
293309
});

src/components/dynamic-input/DynamicInput.vue

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import {
2424
InputType,
2525
BindingObject,
2626
FieldTypes,
27+
ValidationEvent,
28+
InputEvent,
2729
} from '@/core/models';
2830
2931
import { values, keys, isArray, isObject } from '@/core/utils/helpers';
@@ -72,8 +74,10 @@ export default defineComponent({
7274
control: props?.control,
7375
style: props?.control.customStyles,
7476
onChange: valueChange,
75-
onBlur: () => emit('blur', props.control),
76-
onFocus: () => emit('focus', props.control),
77+
onBlur: (e: InputEvent) => emit('blur', e),
78+
onFocus: (e: InputEvent) => emit('focus', e),
79+
onValidate: (validation: ValidationEvent) =>
80+
emit('validate', validation),
7781
};
7882
});
7983
@@ -241,7 +245,11 @@ export default defineComponent({
241245
},
242246
[
243247
`${props?.control?.label}`,
244-
props?.control?.required ? requiredStar : '',
248+
props?.control?.validations?.some(
249+
validator => validator.type === 'required',
250+
)
251+
? requiredStar
252+
: '',
245253
],
246254
)
247255
: null,

src/components/number-input/NumberInput.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { defineComponent, h, PropType } from 'vue';
33
import { FormControl, NumberInput } from '@/core/models';
44
import { useInputEvents } from '@/composables/input-events';
5+
import { useInputValidation } from '@/composables/use-validation';
56
67
const props = {
78
control: Object as PropType<FormControl<NumberInput>>,
@@ -12,6 +13,7 @@ export default defineComponent({
1213
props,
1314
setup(props, { emit }) {
1415
const { onChange, onFocus, onBlur } = useInputEvents(props, emit);
16+
const { isRequired } = useInputValidation(props, emit);
1517
1618
return () =>
1719
h('input', {
@@ -25,12 +27,12 @@ export default defineComponent({
2527
step: props?.control?.step,
2628
disabled: props?.control?.disabled,
2729
placeholder: props?.control?.placeholder,
28-
required: props.control.required,
30+
required: isRequired.value,
2931
readonly: props?.control.readonly,
3032
autocomplete: props.control.autocomplete,
3133
ariaLabel: props.control.ariaLabel,
3234
ariaLabelledBy: props.control.ariaLabelledBy,
33-
ariaRequired: props.control.required,
35+
ariaRequired: isRequired.value,
3436
onFocus,
3537
onBlur,
3638
onChange,

src/components/select-input/SelectInput.vue

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
import { defineComponent, h, PropType, computed } from 'vue';
33
import { FormControl, SelectInput } from '@/core/models';
44
import { useInputEvents } from '@/composables/input-events';
5-
import { isArray, isObject } from '@/core/utils/helpers';
5+
import { isObject } from '@/core/utils/helpers';
6+
import { useInputValidation } from '@/composables/use-validation';
67
78
const props = {
89
control: Object as PropType<FormControl<SelectInput>>,
@@ -14,6 +15,7 @@ export default defineComponent({
1415
setup(props, { emit }) {
1516
return () => {
1617
const { onChange, onFocus, onBlur } = useInputEvents(props, emit);
18+
const { isRequired } = useInputValidation(props, emit);
1719
1820
const formattedOptions = computed(() => {
1921
if (isObject(props?.control?.options)) {
@@ -34,11 +36,11 @@ export default defineComponent({
3436
value: props?.control?.value,
3537
disabled: props?.control?.disabled,
3638
placeholder: props?.control?.placeholder,
37-
required: props.control.required,
39+
required: isRequired.value,
3840
readonly: props?.control.readonly,
3941
ariaLabel: props.control.ariaLabel,
4042
ariaLabelledBy: props.control.ariaLabelledBy,
41-
ariaRequired: props.control.required,
43+
ariaRequired: isRequired.value,
4244
onFocus,
4345
onBlur,
4446
onChange,

src/components/text-area-input/TextAreaInput.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { defineComponent, h, PropType } from 'vue';
33
import { FormControl, TextAreaInput } from '@/core/models';
44
import { useInputEvents } from '@/composables/input-events';
5+
import { useInputValidation } from '@/composables/use-validation';
56
const props = {
67
control: Object as PropType<FormControl<TextAreaInput>>,
78
};
@@ -11,6 +12,7 @@ export default defineComponent({
1112
props,
1213
setup(props, { emit }) {
1314
const { onChange, onFocus, onBlur } = useInputEvents(props, emit);
15+
const { isRequired } = useInputValidation(props, emit);
1416
1517
return () =>
1618
h('textarea', {
@@ -22,12 +24,12 @@ export default defineComponent({
2224
cols: props?.control?.cols,
2325
disabled: props?.control?.disabled,
2426
placeholder: props?.control?.placeholder,
25-
required: props.control.required,
27+
required: isRequired.value,
2628
autocomplete: props.control.autocomplete,
2729
readonly: props?.control.readonly,
2830
ariaLabel: props.control.ariaLabel,
2931
ariaLabelledBy: props.control.ariaLabelledBy,
30-
ariaRequired: props.control.required,
32+
ariaRequired: isRequired.value,
3133
onFocus,
3234
onBlur,
3335
onChange,

src/components/text-input/TextInput.vue

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
UrlInput,
1010
} from '@/core/models';
1111
import { useInputEvents } from '@/composables/input-events';
12+
import { useInputValidation } from '@/composables/use-validation';
1213
1314
const props = {
1415
control: Object as PropType<
@@ -20,20 +21,25 @@ export default defineComponent({
2021
name: 'asTextInput',
2122
props,
2223
setup(props, { emit }) {
23-
const { onChange, onFocus, onBlur } = useInputEvents(props, emit);
24+
const { onChange, onFocus, onBlur, getClasses } = useInputEvents(
25+
props,
26+
emit,
27+
);
28+
const { isRequired } = useInputValidation(props, emit);
29+
2430
return () =>
2531
h('input', {
2632
id: props.control.name,
2733
name: props.control.name || '',
2834
type: props.control.type,
29-
class: ['form-control'],
35+
class: getClasses.value,
3036
value: props.control.value,
3137
disabled: props.control.disabled,
3238
placeholder: props.control.placeholder,
33-
required: props.control.required,
39+
required: isRequired.value,
3440
readonly: props?.control.readonly,
3541
autocomplete: props.control.autocomplete,
36-
ariaRequired: props.control.required,
42+
ariaRequired: isRequired.value,
3743
ariaLabel: props.control.ariaLabel,
3844
ariaLabelledBy: props.control.ariaLabelledBy,
3945
onFocus,

src/composables/input-events.ts

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1-
/* eslint-disable */
1+
import { computed, watch } from 'vue';
2+
import { hasValue, isArray, isObject } from '../core/utils/helpers';
23

3-
import { watch } from 'vue';
4-
import { hasValue } from '../core/utils/helpers';
4+
import { useInputValidation } from '@/composables/use-validation';
5+
import { BindingObject } from '..';
56

67
export function useInputEvents(props: any, emit: any) {
8+
const { validate } = useInputValidation(props, emit);
9+
710
function onChange($event): void {
811
if (props.control && hasValue($event.target.value)) {
912
$event.stopImmediatePropagation();
1013

14+
validate();
1115
emit('change', {
1216
name: props.control.name,
13-
value: $event.target.value
17+
value: $event.target.value,
1418
});
1519
}
1620
}
@@ -25,25 +29,41 @@ export function useInputEvents(props: any, emit: any) {
2529
}
2630
}
2731
function onFocus(): void {
28-
emit('focus');
32+
emit('focus', { name: props.control.name });
2933
}
3034
function onBlur(): void {
31-
emit('blur');
35+
emit('blur', { name: props.control.name });
36+
validate();
3237
}
3338

34-
watch(() => props?.control?.value, (curr, prev) => {
35-
if(prev === undefined && hasValue(curr)) {
36-
emit('change', {
37-
name: props.control.name,
38-
value: props.control.value
39-
});
39+
const getClasses = computed(() => {
40+
const classes = ['form-control'];
41+
if (isArray(props.control.customClass)) {
42+
return [...classes, ...(props.control.customClass as BindingObject[])];
43+
}
44+
if (isObject(props.control.customClass)) {
45+
return [...classes, props.control.customClass];
4046
}
41-
})
47+
return [classes, props.control.customClass];
48+
});
49+
50+
watch(
51+
() => props?.control?.value,
52+
(curr, prev) => {
53+
if (prev === undefined && hasValue(curr)) {
54+
emit('change', {
55+
name: props.control.name,
56+
value: props.control.value,
57+
});
58+
}
59+
},
60+
);
4261

4362
return {
4463
onFocus,
4564
onChange,
4665
onBlur,
4766
onCheck,
67+
getClasses,
4868
};
4969
}

0 commit comments

Comments
 (0)