skip to Main Content

On my page, I have an "edit" button on my page, when I click on it I can edit few things and then save or close without saving.

I want to add a loading bar (like the page is loading) when the user clicks on the "save" button before showing the result.

What I have now is :

the user clicks on the "edit" button, some data become editable and he gets a "save" and a "close" button (that closes without saving), if he clicks on the "save" button the page will reload with the edited data and a notification message "updated" is shown (or an error message if the update didn’t go through).

What I would like to have :

when the user clicks on the "save" button after making his edits, the page will show a loading bar (as to show it’s loading the data) for a few seconds (let’s say for example 5 seconds, if the request is done by that time, the loading will end but if the request isn’t done yet, the bar will keep loading until it’s done) before showing all the data again and the "updated" notification.

Here’s some of the code I have related to this:

My buttons:

             <v-col cols="3">
              <v-btn
                v-if="editGroup"
                light
                class="mx-2"
                @click="editGroup = false"
              >
                <v-icon left>mdi-close</v-icon>
                Close
              </v-btn>
              <v-btn
                v-if="editGroup" 
                light 
                class="mx-2" 
                @click="clickSave"
                >
                <v-icon left>mdi-content-save</v-icon>
                Save
              </v-btn>
              <v-btn
                v-if="
                  canShowButton(['administrateur', 'configurateur']) &&
                  !editGroup
                "
                light
                class="mx-2"
                @click="editGroup = true"
              >
                <v-icon left>mdi-pencil</v-icon>
                Edit
              </v-btn>
            </v-col>

And my edit fuction :

 async clickSave() {
  this.editGroup = false
  this.loading = true

  let uri = this.endpointPrefix + '/groups/' + this.groupid
  let dat = {
    name: this.groupDataEdited.name,
    description: this.groupDataEdited.description,
    idTariff: this.groupDataEdited.idTariff,
    idEvseToAdd: [],
  }
  await this.$axios.$put(uri, dat)
    .then(() => {
        this.$nuxt.$emit('show-notification', {
          text: 'updated',
          color: 'green',
        })
        this.loadData(this.groupid)
    })
    .catch((error) => {
      this.$nuxt.$emit('show-notification', {
        text:
          'could not be updated !' +
          error.response.data,
        color: 'red',
      })
    })
    .finally(() => (this.loading = false))              
  setTimeout(() => {
    this.loading = false        
  }, 5000);
},

As for the progress bar, I am using vuetify progress circular like this :

   <v-progress-circular
    v-show="loading"
    indeterminate
    color="primary">
   </v-progress-circular>

But I still haven’t figured out at what point of my code should I put this component yet to have it shown in all of my page, I tried putting it before the buttons but it doesn’t work at all (nothing happens) and if I put it inside the buttons it keeps loading forever.

2

Answers


  1. This is what state management such as pinia is for. If you set up a pinia store like so:

    import { defineStore } from "pinia"
    
    export enum PageState {
      LOADING,
      LOADED,
      ERROR
    }
    
    export const usePageStore = defineStore({
      state: () => ({
        pageState: PageState.LOADED
      }),
      getters: {},
      actions: {
        saveData(endpointPrefix, groupId, newData) {
          try {
            // Put all your URL building/data calling work here
          } catch (e) {
            throw new Error(e)
          }
        },
      },
    })

    Then, instead of doing all the data work in your vue script, you just do this part:

    pageStore = usePageStore()
    
    async clickSave() {
      try {
        pageStore.pageState = PageState.LOADING
        const response = await pageStore.getData(this.endpointPrefix, this.groupId, dat)
        this.$nuxt.$emit('show-notification', {
              text: 'updated',
              color: 'green',
            })
        this.loadData(this.groupid)
        pageStore.pageState = PageState.LOADED
      } catch (e) {
        pageStore.pageState = PageState.ERROR
         this.$nuxt.$emit('show-notification', {
            text:
              'could not be updated !' +
              error.response.data,
            color: 'red',
          })
      }  
    }

    Then your main, overarching page view can access the same store, watch that variable, and change according to show whatever you need.

    Login or Signup to reply.
  2. Your best bet is going to probably be to put in an overlay, which can be placed at the very bottom of your root element.

    As a rough example:

             <v-col cols="3">
              <v-btn
                v-if="editGroup"
                light
                class="mx-2"
                @click="editGroup = false"
              >
                <v-icon left>mdi-close</v-icon>
                Close
              </v-btn>
              <v-btn
                v-if="editGroup" 
                light 
                class="mx-2" 
                @click="clickSave"
                >
                <v-icon left>mdi-content-save</v-icon>
                Save
              </v-btn>
              <v-btn
                v-if="
                  canShowButton(['administrateur', 'configurateur']) &&
                  !editGroup
                "
                light
                class="mx-2"
                @click="editGroup = true"
              >
                <v-icon left>mdi-pencil</v-icon>
                Edit
              </v-btn>
              <--The overlay will not display when loading is false-->
              <v-overlay v-model="loading">
                <v-progress-circular
                indeterminate
                color="primary">
                </v-progress-circular> 
              </v-overlay>
            </v-col>
    

    The overlay will cover the entire screen until the loading prop is marked as false

    https://vuetifyjs.com/en/components/overlays/

    Edit:

    Additionally, loading will have to be set in the data section of your page when you first load in.

    <script>
      export default {
        data: () => ({
          loading: false,
        }),
        },
      }
    </script>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search