Menu Close

Developing Vue Apps with the Quasar Library — File Upload Progress

Quasar is a popular Vue UI library for developing good looking Vue apps.

In this article, we’ll take a look at how to create Vue apps with the Quasar UI library.

File Upload Progress

We can add file upload progress display to the file picker with the q-file component.

To do this, we write:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-layout
        view="lHh Lpr lFf"
        container
        style="height: 100vh;"
        class="shadow-2 rounded-borders"
      >
        <div class="q-pa-md">
          <q-file
            :value="files"
            @input="updateFiles"
            label="Pick files"
            outlined
            multiple
            :clearable="!isUploading"
            style="max-width: 400px;"
          >
            <template v-slot:file="{ index, file }">
              <q-chip
                class="full-width q-my-xs"
                :removable="isUploading && uploadProgress[index].percent < 1"
                square
              >
                <q-linear-progress
                  class="absolute-full full-height"
                  :value="uploadProgress[index].percent"
                  :color="uploadProgress[index].color"
                  track-color="grey-2"
                >
                </q-linear-progress>

                <q-avatar>
                  <q-icon :name="uploadProgress[index].icon"></q-icon>
                </q-avatar>

                <div class="ellipsis relative-position">
                  {{ file.name }}
                </div>

                <q-tooltip>
                  {{ file.name }}
                </q-tooltip>
              </q-chip>
            </template>

            <template v-slot:after v-if="canUpload">
              <q-btn
                color="primary"
                dense
                icon="cloud_upload"
                round
                @click="upload"
                :disable="!canUpload"
                :loading="isUploading"
              >
              </q-btn>
            </template>
          </q-file>
        </div>
      </q-layout>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {
          files: null,
          uploadProgress: [],
          uploading: null,
          isUploading: false
        },
        computed: {
          isUploading() {
            return this.uploading !== null;
          },

          canUpload() {
            return this.files !== null;
          }
        },
        methods: {
          updateFiles(files) {
            this.files = files;
            this.uploadProgress = (files || []).map((file) => ({
              error: false,
              color: "green-1",
              percent: 0,
              icon:
                file.type.indexOf("video/") === 0
                  ? "movie"
                  : file.type.indexOf("image/") === 0
                  ? "photo"
                  : file.type.indexOf("audio/") === 0
                  ? "audiotrack"
                  : "insert_drive_file"
            }));
          },
          upload() {
            clearTimeout(this.uploading);

            const allDone = this.uploadProgress.every(
              (progress) => progress.percent === 1
            );

            this.uploadProgress = this.uploadProgress.map((progress) => ({
              ...progress,
              error: false,
              color: "green-2",
              percent: allDone === true ? 0 : progress.percent
            }));

            this.__updateUploadProgress();
          },

          __updateUploadProgress() {
            let done = true;

            this.uploadProgress = this.uploadProgress.map((progress) => {
              if (progress.percent === 1 || progress.error === true) {
                return progress;
              }

              const percent = Math.min(
                1,
                progress.percent + Math.random() / 10
              );
              const error = percent < 1 && Math.random() > 0.95;

              if (error === false && percent < 1 && done === true) {
                done = false;
              }

              return {
                ...progress,
                error,
                color: error === true ? "red-2" : "green-2",
                percent
              };
            });

            this.uploading =
              done !== true
                ? setTimeout(this.__updateUploadProgress, 300)
                : null;
          }
        },
        beforeDestroy() {
          clearTimeout(this.uploading);
        }
      });
    </script>
  </body>
</html>

We listen for file changes with the updateFiles method.

In the method, we get the files from the parameter and then call map to return an array of object to display the icons we want.

The upload method is called when we click on the button on the right.

We get the progress from the uploadProgress property and then update it by getting the progress from the progress.percent property.

We set the percent from in the method and set the as the value of the value prop in the q-linear-progress component to display the progress.

Conclusion

We can add a progress bar to the file input to show file upload progress with Quasar.

Posted in vue