搜尋

首頁  >  問答  >  主體

Vue 3 可重複使用錯誤處理和可重複使用「useForm」中的handleSubmit使用組合 API 的函數

在最近的網頁應用程式中,我們有許多具有相同提交結構的表單:

  1. 基於 isSubmitting 變數停用表單和提交按鈕
  2. 驗證輸入欄位(我們使用 Yup
  3. 如果驗證失敗:將 isSubmitting 設定回 false 設定並在輸入欄位上顯示 validationErrors
  4. 如果驗證成功:將帶有表單資料的 post 請求傳送到 api
  5. 如果 api 關閉或傳回錯誤,則顯示一般錯誤

我嘗試過使用 vue 3 中的組合 api 進行一些操作。

登入.vue

#
<template>
    <div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8">
        <div class="sm:mx-auto sm:w-full sm:max-w-md">
            <h1 class="text-3xl text-center text-gray-900">{{ t('sign_in_account', 1) }}</h1>
        </div>

        <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
            <div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
                <form @submit.prevent="handleSubmit">
                    <fieldset :disabled="isSubmitting" class="space-y-6">
                        <MessageBox v-if="errors.general" :title="errors.general" :messages="errors.messages" />
                        <Input :label="t('email', 1)" type="text" id="email" v-model="user.email" :error="errors.email" />
                        <Password :label="t('password', 1)" type="password" id="password" v-model="user.password" :error="errors.password" />

                        <div class="text-sm text-right">
                            <router-link class="font-medium text-indigo-600 hover:text-indigo-500" :to="forgotPassword">{{ t('forgot_password', 1) }}</router-link>
                        </div>

                        <SubmitButton class="w-full" :label="t('sign_in', 1)" :submittingLabel="t('sign_in_loader', 1)" :isSubmitting="isSubmitting" />
                    </fieldset>
                </form>
            </div>
        </div>
    </div>
</template>

<script>
    import { ref } from 'vue';
    import { useStore } from 'vuex';
    import { useI18n } from 'vue-i18n';

    import useForm from '@/use/useForm';
    import { validateEmail, LoginValidationSchema } from '@/utils/validators';

    export default {
        setup() {
            const store = useStore();
            const { t } = useI18n({ useScope: 'global' });

            const user = ref({
                email: '',
                password: '',
            });

            const { handleSubmit, isSubmitting, errors } = useForm(user, LoginValidationSchema, handleLogin);

            async function handleLogin(values) {
                try {
                    return await store.dispatch('auth/login', values);
                } catch (error) {
                    if (error.response) {
                        console.log(error.reponse);
                        if (error.response.status == 422) {
                            errors.value = {
                                general: `${t('unable_to_login', 1)}<br /> ${t('fix_and_retry', 1)}`,
                                messages: Object.values(error.response.data.errors).flat(),
                            };
                        } else if (error.response.data.message) {
                            errors.value = {
                                general: error.response.data.message,
                            };
                        } else {
                            errors.value = {
                                general: `${t('unknown_error', 1)}<br /> ${t('please_try_agin', 1)}`,
                            };
                        }
                    } else if (error.request) {
                        console.log(error.request);
                        errors.value = {
                            general: `${t('unknown_error', 1)}<br /> ${t('please_try_agin', 1)}`,
                        };
                    } else {
                        errors.value = {
                            general: `${t('unknown_error', 1)}<br /> ${t('please_try_agin', 1)}`,
                        };
                    }

                    return;
                }
            }

            return { t, user, handleSubmit, isSubmitting, errors };
        },
        computed: {
            forgotPassword() {
                return validateEmail(this.user.email) ? { name: 'forgotPassword', query: { email: this.user.email } } : { name: 'forgotPassword' };
            },
        },
    };
</script>

useForm.js

#
import { ref, watch } from 'vue';

export default function useForm(initialValues, validationSchema, callback) {
    let values = ref(initialValues);
    let isSubmitting = ref(false);
    let errors = ref({});

    async function handleSubmit() {
        try {
            errors.value = {};
            await validationSchema.validate(values.value, { abortEarly: false });
            isSubmitting.value = true;
        } catch (err) {
            console.log('In the catch');
            isSubmitting.value = false;

            err.inner.forEach((error) => {
                errors.value = { ...errors.value, [error.path]: error.message };
            });
        }
    }

    watch(isSubmitting, () => {
        if (Object.keys(errors.value).length === 0 && isSubmitting.value) {
            callback(values);
            isSubmitting.value = false;
        } else {
            isSubmitting.value = false;
        }
    });

    return { handleSubmit, isSubmitting, errors };
}

這在某種程度上有效,但我錯過了兩件事。在 useForm 中,我想等到回呼完成(成功或失敗)才能將 isSubmitting 設定回 false。承諾是做到這一點的好方法嗎?還有更好的方法嗎?其次,我想要一個可重複使用的方法來處理 Login.vue 中的錯誤。有什麼建議如何處理這個問題嗎?

P粉020556231P粉020556231237 天前422

全部回覆(1)我來回復

  • P粉301523298

    P粉3015232982024-03-28 11:01:09

    關於你的第一個問題- try..catch 語句有第三個名為finally 的語句,該語句總是在try 語句區塊完成後執行

    回答你的第二個問題 - 承諾是處理非同步邏輯的好方法,包括你發送請求的 API 回傳錯誤回應的情況,然後你可以決定如何處理這種情況下的使用者體驗。

    我不太清楚以可重複使用的方式處理Login.vue 中的錯誤是什麼意思,但也許您可以簡單地將空數組道具傳遞給名為formErrorsuseForm 並讓您的useForm.js 發出一個update:modelValue 事件以獲得雙向綁定。

    回覆
    0
  • 取消回覆