vant/src/form/index.js
2020-09-25 14:16:50 +08:00

166 lines
3.7 KiB
JavaScript

import { createNamespace } from '../utils';
import { useExpose } from '../composition/use-expose';
import { useChildren } from '../composition/use-relation';
const [createComponent, bem] = createNamespace('form');
export const FORM_KEY = 'vanForm';
export default createComponent({
props: {
colon: Boolean,
labelWidth: [Number, String],
labelAlign: String,
inputAlign: String,
scrollToError: Boolean,
validateFirst: Boolean,
errorMessageAlign: String,
submitOnEnter: {
type: Boolean,
default: true,
},
validateTrigger: {
type: String,
default: 'onBlur',
},
showError: {
type: Boolean,
default: true,
},
showErrorMessage: {
type: Boolean,
default: true,
},
},
emits: ['submit', 'failed'],
setup(props, { emit, slots }) {
const { children, linkChildren } = useChildren(FORM_KEY);
const validateSeq = () =>
new Promise((resolve, reject) => {
const errors = [];
children
.reduce(
(promise, field) =>
promise.then(() => {
if (!errors.length) {
return field.validate().then((error) => {
if (error) {
errors.push(error);
}
});
}
}),
Promise.resolve()
)
.then(() => {
if (errors.length) {
reject(errors);
} else {
resolve();
}
});
});
const validateAll = () =>
new Promise((resolve, reject) => {
Promise.all(children.map((item) => item.validate())).then((errors) => {
errors = errors.filter((item) => item);
if (errors.length) {
reject(errors);
} else {
resolve();
}
});
});
const validateField = (name) => {
const matched = children.filter((item) => item.name === name);
if (matched.length) {
return new Promise((resolve, reject) => {
matched[0].validate().then((error) => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}
return Promise.reject();
};
const validate = (name) => {
if (name) {
return validateField(name);
}
return props.validateFirst ? validateSeq() : validateAll();
};
const resetValidation = (name) => {
children.forEach((item) => {
if (!name || item.name === name) {
item.resetValidation();
}
});
};
const scrollToField = (name, options) => {
children.some((item) => {
if (item.name === name) {
item.$el.scrollIntoView(options);
return true;
}
return false;
});
};
const getValues = () =>
children.reduce((form, field) => {
form[field.name] = field.formValue.value;
return form;
}, {});
const submit = () => {
const values = getValues();
validate()
.then(() => {
emit('submit', values);
})
.catch((errors) => {
emit('failed', { values, errors });
if (props.scrollToError) {
scrollToField(errors[0].name);
}
});
};
const onSubmit = (event) => {
event.preventDefault();
submit();
};
linkChildren({ props });
useExpose({
submit,
validate,
scrollToField,
resetValidation,
});
return () => (
<form class={bem()} onSubmit={onSubmit}>
{slots.default?.()}
</form>
);
},
});