laravel-precognition-vue
Version:
Laravel Precognition (Vue).
156 lines (155 loc) • 4.89 kB
JavaScript
import { client, createValidator, toSimpleValidationErrors, resolveUrl, resolveMethod, resolveName } from 'laravel-precognition';
import { reactive, ref, toRaw } from 'vue';
import { cloneDeep, get, set } from 'lodash-es';
export { client };
export const useForm = (method, url, inputs, config = {}) => {
/**
* The original data.
*/
const originalData = typeof inputs === 'function' ? cloneDeep(inputs()) : cloneDeep(inputs);
/**
* The original input names.
*/
const originalInputs = Object.keys(originalData);
/**
* Reactive valid state.
*/
const valid = ref([]);
/**
* Reactive touched state.
*/
const touched = ref([]);
/**
* The validator instance.
*/
const validator = createValidator((client) => client[resolveMethod(method)](resolveUrl(url), form.data(), config), originalData)
.on('validatingChanged', () => {
form.validating = validator.validating();
})
.on('validatedChanged', () => {
valid.value = validator.valid();
})
.on('touchedChanged', () => {
touched.value = validator.touched();
})
.on('errorsChanged', () => {
form.hasErrors = validator.hasErrors();
// @ts-expect-error
form.errors = toSimpleValidationErrors(validator.errors());
valid.value = validator.valid();
});
/**
* Resolve the config for a form submission.
*/
const resolveSubmitConfig = (config) => ({
...config,
precognitive: false,
onStart: () => {
form.processing = true;
(config.onStart ?? (() => null))();
},
onFinish: () => {
form.processing = false;
(config.onFinish ?? (() => null))();
},
onValidationError: (response, error) => {
validator.setErrors(response.data.errors);
return config.onValidationError
? config.onValidationError(response)
: Promise.reject(error);
},
});
/**
* Create a new form instance.
*/
let form = {
...cloneDeep(originalData),
data() {
const data = cloneDeep(toRaw(form));
return originalInputs.reduce((carry, name) => ({
...carry,
[name]: data[name],
}), {});
},
setData(data) {
Object.keys(data).forEach((input) => {
// @ts-expect-error
form[input] = data[input];
});
return form;
},
touched(name) {
// @ts-expect-error
return touched.value.includes(name);
},
touch(name) {
validator.touch(name);
return form;
},
validate(name, config) {
if (typeof name === 'object' && !('target' in name)) {
config = name;
name = undefined;
}
if (typeof name === 'undefined') {
validator.validate(config);
}
else {
// @ts-expect-error
name = resolveName(name);
validator.validate(name, get(form.data(), name), config);
}
return form;
},
validating: false,
valid(name) {
// @ts-expect-error
return valid.value.includes(name);
},
invalid(name) {
return typeof form.errors[name] !== 'undefined';
},
errors: {},
hasErrors: false,
setErrors(errors) {
// @ts-expect-error
validator.setErrors(errors);
return form;
},
forgetError(name) {
// @ts-expect-error
validator.forgetError(name);
return form;
},
reset(...names) {
const original = typeof inputs === 'function' ? cloneDeep(inputs()) : cloneDeep(originalData);
if (names.length === 0) {
// @ts-expect-error
originalInputs.forEach((name) => (form[name] = original[name]));
}
else {
names.forEach((name) => set(form, name, get(original, name)));
}
// @ts-expect-error
validator.reset(...names);
return form;
},
setValidationTimeout(duration) {
validator.setTimeout(duration);
return form;
},
processing: false,
async submit(config = {}) {
return client[resolveMethod(method)](resolveUrl(url), form.data(), resolveSubmitConfig(config));
},
validateFiles() {
validator.validateFiles();
return form;
},
validator() {
return validator;
},
};
form = reactive(form);
return form;
};