useSubmitHandler.js 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import { ref, nextTick } from "vue";
  2. export function useSubmitHandler(options = {}) {
  3. const { onSuccess, onError, formRef, scrollFn, containerRef } = options;
  4. const loading = ref(false);
  5. const validationErrors = ref({});
  6. const getFormRefs = () => {
  7. const refs = formRef?.value;
  8. if (!refs) return [];
  9. return Array.isArray(refs) ? refs : [refs];
  10. };
  11. const scrollToFirstError = async () => {
  12. if (!formRef?.value || !scrollFn) return;
  13. await nextTick();
  14. const refsToSearch = getFormRefs();
  15. for (const ref of refsToSearch) {
  16. if (!ref) continue;
  17. const components = ref.getValidationComponents();
  18. if (!components) continue;
  19. const firstErrorComponent = components.find((c) => c.hasError);
  20. if (firstErrorComponent) {
  21. const container = containerRef?.value || containerRef;
  22. firstErrorComponent.focus();
  23. scrollFn(firstErrorComponent, container);
  24. return;
  25. }
  26. }
  27. };
  28. const execute = async (apiCallThunk) => {
  29. loading.value = true;
  30. validationErrors.value = {};
  31. let allValid = true;
  32. const refsToValidate = getFormRefs();
  33. if (refsToValidate.length > 0) {
  34. for (const ref of refsToValidate) {
  35. if (ref) {
  36. const success = await ref.validate(true);
  37. if (!success) {
  38. allValid = false;
  39. }
  40. }
  41. }
  42. }
  43. if (!allValid) {
  44. loading.value = false;
  45. await scrollToFirstError();
  46. throw new Error("Frontend validation failed.");
  47. }
  48. try {
  49. const response = await apiCallThunk();
  50. if (typeof onSuccess === "function") {
  51. await onSuccess(response);
  52. } else {
  53. return response;
  54. }
  55. } catch (error) {
  56. await handleError(error);
  57. } finally {
  58. loading.value = false;
  59. }
  60. };
  61. const handleError = async (error) => {
  62. if (error?.response?.status === 422) {
  63. const errors = error.response.data.errors || {};
  64. for (const key in errors) {
  65. const message = errors[key][0];
  66. validationErrors.value[key] = message;
  67. }
  68. await scrollToFirstError();
  69. }
  70. await nextTick();
  71. if (typeof onError === "function") {
  72. await onError(error);
  73. } else {
  74. throw error;
  75. }
  76. };
  77. return {
  78. loading,
  79. validationErrors,
  80. execute,
  81. };
  82. }