Step 01: We will introduce new reactive variable isLoading in issues.js composable inside storeIssue() method like below.
export default function useIssues() { ... const isLoading = ref(false) const storeIssue = async (issue) => { if (isLoading.value) return; isLoading.value = true validationErrors.value = {} // resetting validation errors axios.post('/api/issues', issue) .then(response => { router.push({ name: 'issues.list' }) }) .catch(error => { if (error.response?.data) { validationErrors.value = error.response.data.errors isLoading.value = false } }) } return { issues, getIssues, storeIssue, validationErrors, isLoading } }
Basically it will be false initially. However, when form is submitted it will be true representing form is processing with loading indicator. In case form submission fails isLoading will be back to false status again.
Step 02: After loading isLoading in Create.vue component using that in template like below
// inside script tag <script setup> ... const { storeIssue, validationErrors, isLoading } = useIssues()
// inside template tag changing button tag with binding disabled and using v-show, v-if and v-else directives <button :disabled="isLoading" type="submit" class="btn btn-primary bg-dark text-white cursor-not-allowed" style="font-size: 0.98rem;"> <span v-show="isLoading" class="spinner-border" style="width: 1rem; height: 1rem;"> </span> <span v-if="isLoading"> Processing...</span> <span v-else>Save</span> </button>
// outside template tag using class to change styling of spinner-border <style> .spinner-border { width: 10px; height: 10px; } </style>
Now if we submit the form with or without input field data there will be loading indicator inside save button and once isLoading is false the indicator will go back to save button.