Skip to content

Commit 0f70973

Browse files
committed
Added support for configuring validateOnChange and validateOnBlur and enhanced improved css classes configuration
1 parent dc626a3 commit 0f70973

File tree

3 files changed

+76
-43
lines changed

3 files changed

+76
-43
lines changed

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,16 @@ _[demo/Full.svelte](https://github.com/cloudedots-projects/svelte-forms/blob/mas
116116
}),
117117
}),
118118
}),
119-
validationClasses: {
120-
valid: "is-valid", // CSS class added to valid form controls
121-
invalid: "is-invalid", // CSS class added to invalid form controls
122-
showValid: true, // Add CSS classes to valid form controls
123-
showInvalid: true, // Add CSS classes to invalid form controls
119+
// CSS class validations
120+
css: {
121+
enabled: true, // use CSS classes or not
122+
validClass: "is-valid", // CSS class added to valid form controls
123+
invalidClass: "is-invalid", // CSS class added to invalid form controls
124+
useValid: true, // Add CSS classes to valid form controls
125+
useInvalid: true, // Add CSS classes to invalid form controls
124126
},
127+
validateOnChange: true, // Whether to validate on "change" event of element and form value change
128+
validateOnBlur: true, // Whether to validate on "blur" event of element
125129
});
126130
127131
// Add new user to Users Form Array

demo/Full.svelte

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,16 @@
5050
})
5151
),
5252
}),
53-
validationClasses: {
54-
valid: "is-valid", // CSS class added to valid form controls
55-
invalid: "is-invalid", // CSS class added to invalid form controls
56-
showValid: true, // Add CSS classes to valid form controls
57-
showInvalid: true, // Add CSS classes to invalid form controls
53+
// CSS class
54+
css: {
55+
enabled: true, // use CSS classes or not
56+
validClass: "is-valid", // CSS class added to valid form controls
57+
invalidClass: "is-invalid", // CSS class added to invalid form controls
58+
useValid: true, // Add CSS classes to valid form controls
59+
useInvalid: true, // Add CSS classes to invalid form controls
5860
},
61+
validateOnChange: true, // Whether to validate on "change" event of element and form value change
62+
validateOnBlur: true, // Whether to validate on "blur" event of element
5963
});
6064
6165
// Add new user to Users Form Array
@@ -159,11 +163,11 @@
159163
</form>
160164

161165
<style>
162-
.valid {
166+
.is-valid {
163167
border: 1px solid green;
164168
}
165169
166-
.invalid {
170+
.is-invalid {
167171
border: 1px solid red;
168172
}
169173

src/index.ts

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ type FormState = {
1313
type FormConfigInput<Data = any> = {
1414
initialValues: Data,
1515
validationSchema?: ObjectSchema<any>,
16-
validationClasses?: {
17-
valid?: string,
18-
invalid?: string,
19-
showValid?: boolean,
20-
showInvalid?: boolean,
21-
},
16+
css?: {
17+
enabled?: boolean,
18+
validClass?: string,
19+
invalidClass?: string,
20+
useValid?: boolean,
21+
useInvalid?: boolean,
22+
}
23+
validateOnChange?: boolean,
24+
validateOnBlur?: boolean,
2225
};
2326

2427
function isNullish(value) {
@@ -220,17 +223,23 @@ const getFieldState = (name: string, $state: FormState) => {
220223
return obj[lastSegment] || null;
221224
}
222225

223-
export function createForm<Data>({ initialValues, validationSchema, validationClasses }: FormConfigInput<Data>) {
226+
export function createForm<Data>({ initialValues, validationSchema, css: cssConfig, validateOnChange, validateOnBlur }: FormConfigInput<Data>) {
224227
const form = writable<Data>(initialValues);
225228
const state = writable<FormState>({});
226229
const isValid = writable<boolean>(false);
227230
const isTouched = writable<boolean>(false);
228-
const classes = Object.assign({
229-
valid: 'is-valid',
230-
invalid: 'is-invalid',
231-
showValid: true,
232-
showInvalid: true,
233-
}, validationClasses || {});
231+
const css = {
232+
...{
233+
enabled: true,
234+
validClass: 'is-valid',
235+
invalidClass: 'is-invalid',
236+
useValid: true,
237+
useInvalid: true,
238+
}, ...(cssConfig || {})
239+
};
240+
validateOnChange = typeof validateOnChange !== 'boolean' ? true : validateOnChange;
241+
validateOnBlur = typeof validateOnBlur !== 'boolean' ? true : validateOnBlur;
242+
const cssValidator = writable<number>(0);
234243

235244
createState(initialValues, state, validationSchema);
236245

@@ -241,6 +250,7 @@ export function createForm<Data>({ initialValues, validationSchema, validationCl
241250

242251
const validateForm = (highlight: 'none' | 'errors' | 'all' = 'none') => {
243252
isValid.set(validate(form, validationSchema, state, highlight === 'all', highlight === 'all' || highlight === 'errors'));
253+
css.enabled && !validateOnChange && highlight !== 'none' && cssValidator.update(val => val + 1);
244254
}
245255

246256
form.subscribe(() => {
@@ -258,41 +268,56 @@ export function createForm<Data>({ initialValues, validationSchema, validationCl
258268
}
259269

260270
const formControl = (node: HTMLInputElement, options: any = {}) => {
261-
const changeListender = (event: Event) => {
262-
handleChange(event);
271+
const changeListener = (event: Event) => {
272+
if (validateOnChange) {
273+
handleChange(event);
274+
css.enabled && checkValidity(get(state));
275+
}
276+
}
277+
const blurListener = (event: Event) => {
278+
if (validateOnBlur) {
279+
handleChange(event);
280+
css.enabled && checkValidity(get(state));
281+
}
263282
}
264283
let unsubscribeState: Unsubscriber = null;
284+
let unsubscribeCssValidator: Unsubscriber = null;
265285

266286
const checkValidity = async ($state: FormState) => {
267-
const fieldState = getFieldState(node.name, $state);
268-
const invalid = fieldState?._touched && !!fieldState?._errors?.length;
269-
const valid = fieldState?._touched && !fieldState?._errors?.length;
270-
node.classList.remove(classes.valid);
271-
node.classList.remove(classes.invalid);
272-
if (invalid) {
273-
classes.showInvalid && node.classList.add(classes.invalid);
274-
} else if (valid) {
275-
classes.showValid && node.classList.add(classes.valid);
287+
if (node.name) {
288+
const fieldState = getFieldState(node.name, $state);
289+
const invalid = fieldState?._touched && !!fieldState?._errors?.length;
290+
const valid = fieldState?._touched && !fieldState?._errors?.length;
291+
node.classList.remove(css.validClass);
292+
node.classList.remove(css.invalidClass);
293+
if (invalid) {
294+
css.useInvalid && node.classList.add(css.invalidClass);
295+
} else if (valid) {
296+
css.useValid && node.classList.add(css.validClass);
297+
}
276298
}
277-
278299
}
279300

280301
if (['input', 'checkbox', 'radio', 'select', 'textarea'].includes(node.tagName)
281302
|| node.contentEditable) {
282-
node.addEventListener('change', changeListender);
283-
node.addEventListener('blur', changeListender);
303+
node.addEventListener('change', changeListener);
304+
node.addEventListener('blur', blurListener);
305+
284306
if (node.name) {
285307
unsubscribeState = state.subscribe($state => {
286-
checkValidity($state);
308+
css.enabled && validateOnChange && checkValidity($state);
309+
});
310+
unsubscribeCssValidator = cssValidator.subscribe(() => {
311+
checkValidity(get(state));
287312
});
288313
}
289314
}
290315
return {
291-
update(options: any = {}) { },
292316
destroy() {
293-
node.removeEventListener('change', changeListender);
294-
node.removeEventListener('blur', changeListender);
317+
node.removeEventListener('change', changeListener);
318+
node.removeEventListener('blur', blurListener);
295319
unsubscribeState && unsubscribeState();
320+
unsubscribeCssValidator && unsubscribeCssValidator();
296321
}
297322
}
298323
}

0 commit comments

Comments
 (0)