If the form takes longer to submit, then we need to show some kind of loading indicator and disable the submit button so the user wouldn't hit it twice.
It's pretty easy to achieve. In the posts Composable, we need a variable isLoading
which will be true
or false
. When the button is clicked we just set it as true
.
Also, we will reset the validation errors from the previous lesson. And if there are any errors, we need to set isLoading
to false
so that we could submit the form again.
resources/js/composables/posts.js:
import { ref } from 'vue'import { useRouter } from 'vue-router' export default function usePosts() { const posts = ref({}) const router = useRouter() const validationErrors = ref({}) const isLoading = ref(false) // ... const storePost = async (post) => { if (isLoading.value) return; isLoading.value = true validationErrors.value = {} axios.post('/api/posts', post) .then(response => { router.push({ name: 'posts.index' }) }) .catch(error => { if (error.response?.data) { validationErrors.value = error.response.data.errors isLoading.value = false } }) } return { posts, getPosts, storePost, validationErrors } return { posts, getPosts, storePost, validationErrors, isLoading } }
And in the PostsCreate
Vue component for the button we need to do a couple of things:
disabled
to the isLoading
variable.v-show
directive show the loading spinner.v-if
: if isLoading
is true
then show the text Processing...
, otherwise using v-else
show the Save
text.resources/js/components/Posts/Create.vue:
<template> <form @submit.prevent="storePost(post)"> // ... <!-- Buttons --> <div class="mt-4"> <button class="rounded-md bg-indigo-600 px-3 py-2 text-sm uppercase text-white">Save</button> <button :disabled="isLoading" class="inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 text-sm uppercase text-white disabled:opacity-75 disabled:cursor-not-allowed"> <span v-show="isLoading" class="inline-block animate-spin w-4 h-4 mr-2 border-t-2 border-t-white border-r-2 border-r-white border-b-2 border-b-white border-l-2 border-l-blue-600 rounded-full"></span> <span v-if="isLoading">Processing...</span> <span v-else>Save</span> </button> </div> </form></template> <script setup>import { onMounted, reactive } from 'vue';import useCategories from '@/composables/categories';import usePosts from '@/composables/posts'; const post = reactive({ title: '', content: '', category_id: ''}) const { categories, getCategories } = useCategories()const { storePost, validationErrors } = usePosts() const { storePost, validationErrors, isLoading } = usePosts() onMounted(() => { getCategories()})</script>
That's it. This is how easy it is to add a loading indicator.