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

Commit 1ce12da

Browse files
authored
Merge pull request #204 from asigloo/bugfix/validators-errors
Bugfix/validators errors
2 parents f3dc2dd + 77963da commit 1ce12da

File tree

13 files changed

+79
-78
lines changed

13 files changed

+79
-78
lines changed

dev/typescript/App.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<h1 class="title mb-16 text-bg">{{ title }}</h1>
1111
<dynamic-form
1212
:form="form"
13-
@submited="handleSubmit"
13+
@submitted="handleSubmit"
1414
@change="valueChanged"
1515
@error="handleError"
1616
>
@@ -34,7 +34,7 @@
3434
</template>
3535
</dynamic-form>
3636
<button
37-
class="btn bg-teal-500 text-white hover:bg-teal-700 mt-4"
37+
class="btn bg-green-500 text-white hover:bg-green-700 mt-4"
3838
submit="true"
3939
:form="form?.id"
4040
>

src/components/checkbox-input/CheckboxInput.vue

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import { useInputValidation } from '@/composables/use-validation';
66
77
const props = {
88
control: Object as PropType<FormControl<CheckboxInput>>,
9+
forceValidation: {
10+
type: Boolean,
11+
default: false,
12+
},
913
};
1014
1115
export default defineComponent({

src/components/dynamic-form/DynamicForm.vue

+31-46
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
:key="control.name"
1313
:control="control"
1414
:submited="submited"
15+
:forceValidation="forceValidation"
1516
@change="valueChange"
1617
@blur="onBlur"
1718
@validate="onValidate"
@@ -36,6 +37,8 @@
3637
</template>
3738

3839
<script lang="ts">
40+
import { nextTick } from 'vue';
41+
3942
import {
4043
defineComponent,
4144
PropType,
@@ -61,6 +64,7 @@ import {
6164
} from '@/core/models';
6265
import { dynamicFormsSymbol } from '@/useApi';
6366
import { deepClone, hasValue, removeEmpty } from '@/core/utils/helpers';
67+
import { FieldControl } from '@/core/factories';
6468
6569
const props = {
6670
form: {
@@ -73,12 +77,6 @@ const components = {
7377
DynamicInput,
7478
};
7579
76-
const EMPTY_CONTROL = {
77-
dirty: false,
78-
touched: false,
79-
valid: true,
80-
};
81-
8280
/* const AVAILABLE_THEMES = ['default', 'material'];
8381
*/
8482
export default defineComponent({
@@ -90,7 +88,7 @@ export default defineComponent({
9088
const cache = deepClone(toRaw(props.form.fields));
9189
9290
const controls: Ref<FormControl<InputType>[]> = ref([]);
93-
const submited = ref(false);
91+
const forceValidation = ref(false);
9492
9593
const deNormalizedScopedSlots = computed(() => Object.keys(ctx.slots));
9694
@@ -168,17 +166,15 @@ export default defineComponent({
168166
Object.entries(props.form?.fields).map(
169167
([key, field]: [string, InputType]) =>
170168
empty
171-
? ({
169+
? FieldControl({
172170
...field,
173171
name: key,
174172
value: null,
175-
...EMPTY_CONTROL,
176-
} as FormControl<InputType>)
177-
: ({
173+
})
174+
: FieldControl({
178175
...field,
179176
name: key,
180-
...EMPTY_CONTROL,
181-
} as FormControl<InputType>),
177+
}),
182178
) || [];
183179
if (props.form.fieldOrder) {
184180
controls.value = controlArray.sort(
@@ -225,61 +221,49 @@ export default defineComponent({
225221
}
226222
}
227223
228-
/* function validateControl(control: FormControl<InputType>) {
229-
if (control.validations) {
230-
const validation = control.validations.reduce((prev, curr) => {
231-
const val =
232-
typeof curr.validator === 'function'
233-
? curr.validator(control)
234-
: null;
235-
if (val !== null) {
236-
const [key, value] = Object.entries(val)[0];
237-
const obj = {};
238-
obj[key] = {
239-
value,
240-
text: curr.text,
241-
};
242-
return {
243-
...prev,
244-
...obj,
245-
};
246-
}
247-
return {
248-
...prev,
249-
};
250-
}, {});
251-
control.errors = validation;
252-
control.valid = Object.keys(validation).length === 0;
253-
}
254-
} */
255-
256224
function detectChanges(fields) {
257225
const changes = diff(cache, deepClone(fields));
258226
Object.entries(changes).forEach(([key, value]) => {
259227
let ctrl = findControlByName(key);
260228
if (ctrl) {
261229
Object.entries(value).forEach(([change, newValue]) => {
262-
ctrl[change] = newValue;
230+
if (change === 'options' || change === 'validations') {
231+
Object.entries(newValue).forEach(([optKey, optValue]) => {
232+
ctrl[change][optKey] = {
233+
...ctrl[change][optKey],
234+
...optValue,
235+
};
236+
});
237+
} else {
238+
ctrl[change] = newValue;
239+
}
263240
});
264241
}
265242
});
266243
}
267244
268245
function resetForm() {
269246
mapControls(true);
247+
forceValidation.value = false;
270248
}
271249
272-
function handleSubmit() {
273-
submited.value = true;
250+
async function handleSubmit() {
251+
validateAll();
252+
253+
await nextTick();
274254
275255
if (isValid.value) {
276-
ctx.emit('submited', formValues);
256+
ctx.emit('submitted', formValues);
277257
resetForm();
278258
} else {
279259
ctx.emit('error', formValues);
280260
}
281261
}
282262
263+
function validateAll() {
264+
forceValidation.value = true;
265+
}
266+
283267
watch(
284268
() => props.form.fields,
285269
fields => {
@@ -303,10 +287,11 @@ export default defineComponent({
303287
errors,
304288
deNormalizedScopedSlots,
305289
normalizedControls,
306-
submited,
307290
formattedOptions,
308291
onBlur,
309292
onValidate,
293+
forceValidation,
294+
validateAll,
310295
};
311296
},
312297
});

src/components/dynamic-input/DynamicInput.vue

+4-17
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@ import {
2828
InputEvent,
2929
} from '@/core/models';
3030
31-
import { values, isArray, isObject } from '@/core/utils/helpers';
31+
import { isArray, isObject } from '@/core/utils/helpers';
3232
import { useInputEvents } from '@/composables/input-events';
33-
import { dynamicFormsSymbol } from '@/useApi';
3433
3534
const components = {
3635
TextInputComponent,
@@ -46,9 +45,9 @@ const props = {
4645
type: Object as PropType<FormControl<InputType>>,
4746
required: true,
4847
},
49-
submited: {
48+
forceValidation: {
5049
type: Boolean,
51-
required: true,
50+
default: false,
5251
},
5352
};
5453
@@ -65,7 +64,6 @@ export default defineComponent({
6564
props,
6665
setup(props, { emit, slots }) {
6766
const { onFocus, onBlur } = useInputEvents(props?.control, emit);
68-
const { options } = inject(dynamicFormsSymbol);
6967
7068
let component;
7169
@@ -78,6 +76,7 @@ export default defineComponent({
7876
onFocus: (e: InputEvent) => emit('focus', e),
7977
onValidate: (validation: ValidationEvent) =>
8078
emit('validate', validation),
79+
forceValidation: props.forceValidation,
8180
};
8281
});
8382
@@ -105,18 +104,6 @@ export default defineComponent({
105104
return [classes, props?.control?.customClass];
106105
});
107106
108-
const autoValidate = computed(
109-
() => props?.control?.touched && options?.autoValidate,
110-
);
111-
112-
const errorMessages = computed(() => {
113-
const errors = values(props?.control?.errors || {});
114-
if (errors.length > 0 && (props.submited || autoValidate.value)) {
115-
return errors.map(value => value.text);
116-
}
117-
return [];
118-
});
119-
120107
function valueChange($event) {
121108
emit('change', $event);
122109
}

src/components/number-input/NumberInput.vue

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import { useInputValidation } from '@/composables/use-validation';
66
77
const props = {
88
control: Object as PropType<FormControl<NumberInput>>,
9+
forceValidation: {
10+
type: Boolean,
11+
default: false,
12+
},
913
};
1014
1115
export default defineComponent({

src/components/radio-input/RadioInput.vue

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import { useInputValidation } from '@/composables/use-validation';
66
77
const props = {
88
control: Object as PropType<FormControl<RadioInput>>,
9+
forceValidation: {
10+
type: Boolean,
11+
default: false,
12+
},
913
};
1014
1115
export default defineComponent({

src/components/select-input/SelectInput.vue

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import { useInputValidation } from '@/composables/use-validation';
77
88
const props = {
99
control: Object as PropType<FormControl<SelectInput>>,
10+
forceValidation: {
11+
type: Boolean,
12+
default: false,
13+
},
1014
};
1115
1216
export default defineComponent({

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

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import { useInputEvents } from '@/composables/input-events';
55
import { useInputValidation } from '@/composables/use-validation';
66
const props = {
77
control: Object as PropType<FormControl<TextAreaInput>>,
8+
forceValidation: {
9+
type: Boolean,
10+
default: false,
11+
},
812
};
913
1014
export default defineComponent({

src/components/text-input/TextInput.vue

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ const props = {
1515
control: Object as PropType<
1616
FormControl<TextInput | EmailInput | PasswordInput | UrlInput | ColorInput>
1717
>,
18+
forceValidation: {
19+
type: Boolean,
20+
default: false,
21+
},
1822
};
1923
2024
export default defineComponent({

src/composables/use-validation.ts

+13-7
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
import { ErrorMessage } from '@/core/models';
44
import { removeEmpty } from '@/core/utils/helpers';
5-
import { computed, ref } from 'vue';
5+
import { computed, ref, watch } from 'vue';
66

77
export function useInputValidation(props: any, emit: any) {
88
const isPendingValidation = ref(false);
9+
910
const isRequired = computed(() => {
1011
return props.control.validations.some(
1112
validation => validation.type === 'required',
@@ -16,9 +17,9 @@ export function useInputValidation(props: any, emit: any) {
1617
return props.control.validations.length > 0;
1718
});
1819

19-
async function validate(): Promise<void> {
20+
async function validate(force = false): Promise<void> {
2021
if (
21-
(props.control.touched || props.control.dirty) &&
22+
force || (props.control.touched || props.control.dirty) &&
2223
requiresValidation.value
2324
) {
2425
let errors = {};
@@ -36,10 +37,6 @@ export function useInputValidation(props: any, emit: any) {
3637
}
3738
});
3839

39-
console.log({
40-
sync: syncValidations,
41-
async: asyncValidations,
42-
});
4340
if(asyncValidations.length > 0) {
4441
isPendingValidation.value = true;
4542

@@ -112,6 +109,15 @@ export function useInputValidation(props: any, emit: any) {
112109
];
113110
});
114111

112+
watch(
113+
() => props.forceValidation,
114+
value => {
115+
if(value) {
116+
validate(value)
117+
}
118+
},
119+
);
120+
115121
return {
116122
isPendingValidation,
117123
validate,

src/core/factories.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ export const FieldControl = ({
149149
type,
150150
...rest
151151
}: Partial<FormControl<any>>): FormControl<any> => ({
152-
...FieldBase(rest),
152+
...rest,
153153
name,
154154
type,
155155
...EMPTY_CONTROL,

src/core/utils/validators.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const url = (value: string): ValidationErrors => {
4747
};
4848

4949
export const minLength = (minLength: number) => (
50-
value: number,
50+
value: string,
5151
): ValidationErrors => {
5252
if (isEmptyInputValue(value)) {
5353
return { minLength: null }; // don't validate empty values to allow optional controls
@@ -63,14 +63,14 @@ export const minLength = (minLength: number) => (
6363
};
6464

6565
export const maxLength = (maxLength: number) => (
66-
value: number,
66+
value: string,
6767
): ValidationErrors => {
6868
if (isEmptyInputValue(value)) {
69-
return null; // don't validate empty values to allow optional controls
69+
return { maxLength: null }; // don't validate empty values to allow optional controls
7070
}
7171
const length = value ? `${value}`.length : 0;
7272
return {
73-
maxlength:
73+
maxLength:
7474
length > maxLength
7575
? { requiredLength: maxLength, actualLength: length }
7676
: null,

0 commit comments

Comments
 (0)