Wait to Upload Drag and Drop Images Before Submit

Introduction

In this article, nosotros will talk almost how to handle file uploads with VueJs. We volition create an images uploader that allow user to upload single or multiple images file by drag and drib or select file dialog.

We will then upload the selected images and display them accordingly. We will also larn to filter the upload file type, for example, we merely allow images, do non allow file type like PDF.

Image uploader

  • Sourcecode: https://github.com/chybie/file-upload-vue
  • Demo: https://vue-file-upload-1126b.firebaseapp.com/

File Upload UI & API

File upload consists of two parts: the UI (front-end) and the API (back-end). We volition be using VueJs to handle the UI part. We demand a backend application to have the uploaded files. You may follow the backend tutorials or download and run either one of these server side awarding to handle file upload for your backend:-

  • File upload with Hapi.js: https://scotch.io/bar-talk/treatment-file-uploads-with-hapi-js, or
  • File upload with Express + Multer: https://scotch.io/tutorials/express-file-uploads-with-multer, or
  • Switch to whatever cloud solution of your option (Amazon S3, Google Drive, etc).

Nosotros will exist using File upload with Hapi.js as our backend throughout this articles. Nosotros will too learn the tricks to enable imitation upload on the front end-terminate.

Setup Project with Vue-Cli

Nosotros will be using vue-cli to scaffold Vue.js projects. We will be using the webpack-simple project template.

                      # install cli            npm            install            vue-cli -m            # then create project, with sass            # follow the instructions to install all necessary dependencies            vue init webpack-uncomplicated file-upload-vue                  

Alright, all set. Let's proceed to create our component.

File Upload Component

We will write our code in App.vue. Remove all the auto-generated lawmaking in the file.

                      <!-- App.vue -->            <!-- HTML Template -->                                          <template              >                                                      <div              id                              =                "app"                            >                                                      <div              class                              =                "container"                            >                        <!--UPLOAD-->                                          <form              enctype                              =                "multipart/course-information"                            novalidate              five-if                              =                "isInitial || isSaving"                            >                                                      <h1              >            Upload images                              </h1              >                                                      <div              class                              =                "dropbox"                            >                                                      <input              type                              =                "file"                            multiple              :proper name                              =                "uploadFieldName"                            :disabled                              =                "isSaving"                            @change                              =                "filesChange($result.target.name, $event.target.files); fileCount = $effect.target.files.length"                            take                              =                "image/*"                            class                              =                "input-file"                            >                                                      <p              v-if                              =                "isInitial"                            >                        Drag your file(due south) hither to begin                              <br              >                        or click to browse                                          </p              >                                                      <p              v-if                              =                "isSaving"                            >                        Uploading {{ fileCount }} files...                                          </p              >                                                      </div              >                                                      </form              >                                                      </div              >                                                      </template              >                        <!-- Javascript -->                                          <script              >                                                                                                          </script              >                        <!-- SASS styling -->                                          <style              lang                              =                "scss"                            >                                                                                                          </style              >                              

Notes:-

  1. Our App.vue component consists of 3 part: template (HTML), script (Javascript) and styles (SASS).
  2. Our template has an upload form.
  3. The form attribute enctype="multipart/course-information" is important. To enable file upload, this aspect must be prepare. Learn more than most enctype here.
  4. We have a file input <input type="file" /> to accept file upload. The property multiple betoken it's allow multiple file upload. Remove information technology for single file upload.
  5. We will handle the file input change event. Whenever the file input change (someone drop or select files), nosotros will trigger the filesChange office and pass in the command name and selected files $issue.target.files, and then upload to server.
  6. We limit the file input to accept images only with the aspect take="image/*".
  7. The file input will be disabled during upload, and then user can only drib / select files over again afterward upload consummate.
  8. We capture the fileCount of the when file changes. Nosotros use the fileCount variable in displaying number of files uploading Uploading {{ fileCount }} files....

Manner our File Upload Component

Now, that's the interesting part. Currently, our component look like this:

File upload component without styling

We need to transform it to expect similar this:

File upload component with styling

Let's fashion information technology!

                      <!-- App.vue -->            ...            <!-- SASS styling -->            <manner lang="scss">            .dropbox {                          outline              :              2px dashed grey;              /              *              the dash box              *              /                                      outline-offset              :              -10px;                          background              :              lightcyan;                          color              :              dimgray;                          padding              :              10px 10px;                          min-pinnacle              :              200px;              /              *              minimum meridian              *              /                                      position              :              relative;                          cursor              :              pointer;            }            .input-file {                          opacity              :              0;              /              *              invisible simply information technology's there!              *              /                                      width              :              100%;                          superlative              :              200px;                          position              :              absolute;                          cursor              :              pointer;            }                          .dropbox              :              hover              {                          background              :              lightblue;              /              *              when mouse over to the drop zone, alter color              *              /                        }            .dropbox p {                          font-size              :              1.2em;                          text-align              :              middle;                          padding              :              50px 0;            }            </way>                  

With only few lines of scss, our component looks prettier now.

Notes:-

  1. Nosotros brand the file input invisible by applying opacity: 0 mode. This doesn't hibernate the file input, it just make information technology invisible.
  2. Then, we mode the file input parent element, the dropbox css class. We make information technology look similar a drop file zone environs with dash.
  3. Then, nosotros align the text within dropbox to center.

File Upload Component Code

Let's go on to lawmaking our component.

                      <            !            --            App.vue            --            >            ...            <            !            --            Javascript            --            >            <script>            import            {            upload            }            from            './file-upload.service'            ;            const            STATUS_INITIAL            =            0            ,            STATUS_SAVING            =            1            ,            STATUS_SUCCESS            =            2            ,            STATUS_FAILED            =            3            ;            consign            default            {            proper name            :            'app'            ,            data            (            )            {            return            {            uploadedFiles            :            [            ]            ,            uploadError            :            null            ,            currentStatus            :            null            ,            uploadFieldName            :            'photos'            }            }            ,            computed            :            {            isInitial            (            )            {            return            this            .currentStatus            ===            STATUS_INITIAL            ;            }            ,            isSaving            (            )            {            return            this            .currentStatus            ===            STATUS_SAVING            ;            }            ,            isSuccess            (            )            {            return            this            .currentStatus            ===            STATUS_SUCCESS            ;            }            ,            isFailed            (            )            {            return            this            .currentStatus            ===            STATUS_FAILED            ;            }            }            ,            methods            :            {            reset            (            )            {            // reset class to initial state            this            .currentStatus            =            STATUS_INITIAL            ;            this            .uploadedFiles            =            [            ]            ;            this            .uploadError            =            null            ;            }            ,            save            (            formData            )            {            // upload data to the server            this            .currentStatus            =            STATUS_SAVING            ;            upload            (formData)            .            and so            (            x            =>            {            this            .uploadedFiles            =            [            ]            .            concat            (x)            ;            this            .currentStatus            =            STATUS_SUCCESS            ;            }            )            .            catch            (            err            =>            {            this            .uploadError            =            err.response;            this            .currentStatus            =            STATUS_FAILED            ;            }            )            ;            }            ,            filesChange            (            fieldName,              fileList            )            {            // handle file changes            const            formData            =            new            FormData            (            )            ;            if            (            !fileList.length)            render            ;            // append the files to FormData            Array            .            from            (            Array            (fileList.length)            .            keys            (            )            )            .            map            (            x            =>            {            formData.            suspend            (fieldName,            fileList[ten]            ,            fileList[x]            .proper name)            ;            }            )            ;            // save it            this            .            salvage            (formData)            ;            }            }            ,            mounted            (            )            {            this            .            reset            (            )            ;            }            ,            }            <            /script>                  

Notes:-

  1. Our component will have a few statuses: STATUS_INITIAL, STATUS_SAVING, STATUS_SUCCESS, STATUS_FAILED, the variable proper noun is pretty expressive themselves.
  2. Later on, we will telephone call the Hapi.js file upload API to upload images, the API accept a field call photos. That's our file input field name.
  3. We handle the file changes with the filesChange office. FileList is an object returned by the files property of the HTML <input> element. Information technology allow us to access the list of files selected with the <input type="file"> element. Learn more [here]((https://developer.mozilla.org/en/docs/Spider web/API/FileList).
  4. We then create a new FormData, and append all our photos files to it. FormData interface provides a way to easily construct a set up of key/value pairs representing course fields and their values. Learn more here.
  5. The save role will call our file upload service (hang on, nosotros will create the service next!). We besides set the status co-ordinate to the result.
  6. mount() is the vue component life bicycle hook. During that point, we will fix our component status to initial state.

File Upload Service

Permit'south go along to create our service. We will be using axios to make HTTP calls.

Install axios

                      # install axios            npm            install            axios --save                  

Service

                      // file-upload.service.js            import            *            as            axios            from            'axios'            ;            const            BASE_URL            =            'http://localhost:3001'            ;            function            upload            (            formData            )            {            const            url            =                          `                              ${                BASE_URL                }                            /photos/upload              `                        ;            render            axios.            mail service            (url,            formData)            // become data            .            and then            (            x            =>            ten.information)            // add together url field            .            then            (            x            =>            10.            map            (            img            =>            Object.            assign            (            {            }            ,            img,            {            url            :                          `                              ${                BASE_URL                }                            /images/                              ${img.id}                            `                        }            )            )            )            ;            }            export            {            upload            }                  

Nada much, the code is pretty expressive itself. We upload the files, wait for the issue, map it appropriately.

Y'all may run the application now with npm run dev command. Try uploading a couple of images, and information technology's working! (Remember to start your backend server)

Brandish Success and Failed Result

We can upload the files successfully at present. Yet, there's no indication in UI. Permit's update our HTML template.

                      <!-- App.vue -->            <!-- HTML Template -->                                          <template              >                                                      <div              id                              =                "app"                            >                                                      <div              class                              =                "container"                            >                        ...form...            <!--SUCCESS-->                                          <div              v-if                              =                "isSuccess"                            >                                                      <h2              >            Uploaded {{ uploadedFiles.length }} file(s) successfully.                              </h2              >                                                      <p              >                                                      <a              href                              =                "javascript:void(0)"                            @click                              =                "reset()"                            >            Upload over again                              </a              >                                                      </p              >                                                      <ul              grade                              =                "list-unstyled"                            >                                                      <li              five-for                              =                "item in uploadedFiles"                            >                                                      <img              :src                              =                "particular.url"                            course                              =                "img-responsive img-thumbnail"                            :alt                              =                "particular.originalName"                            >                                                      </li              >                                                      </ul              >                                                      </div              >                        <!--FAILED-->                                          <div              v-if                              =                "isFailed"                            >                                                      <h2              >            Uploaded failed.                              </h2              >                                                      <p              >                                                      <a              href                              =                "javascript:void(0)"                            @click                              =                "reset()"                            >            Try again                              </a              >                                                      </p              >                                                      <pre              >            {{ uploadError }}                              </pre              >                                                      </div              >                                                      </div              >                                                      </div              >                                                      </template              >                              

Notes:-

  1. Display the uploaded prototype when upload successfully.
  2. Display the fault message when upload failed.

Fake the Upload in Front-end

If you are lazy to start the back-terminate awarding (Hapi, Express, etc) to handle file upload. Here is a fake service to supercede the file upload service.

                      // file-upload.fake.service.js            function            upload            (            formData            )            {            const            photos            =            formData.            getAll            (            'photos'            )            ;            const            promises            =            photos.            map            (            (            x            )            =>            getImage            (x)            .            and then            (            img            =>            (            {            id            :            img,            originalName            :            x.name,            fileName            :            10.proper name,            url            :            img            }            )            )            )            ;            render            Hope.            all            (promises)            ;            }            function            getImage            (            file            )            {            return            new            Promise            (            (            resolve,              decline            )            =>            {            const            fReader            =            new            FileReader            (            )            ;            const            img            =            document.            createElement            (            'img'            )            ;            fReader.            onload            =            (            )            =>            {            img.src            =            fReader.result;            resolve            (            getBase64Image            (img)            )            ;            }            fReader.            readAsDataURL            (file)            ;            }            )            }            office            getBase64Image            (            img            )            {            const            canvas            =            document.            createElement            (            'canvas'            )            ;            sail.width            =            img.width;            canvas.height            =            img.top;            const            ctx            =            sheet.            getContext            (            '2d'            )            ;            ctx.            drawImage            (img,            0            ,            0            )            ;            const            dataURL            =            canvas.            toDataURL            (            'image/png'            )            ;            return            dataURL;            }            consign            {            upload            }                  

Came across this solution in this Stackoverflow postal service. Pretty useful. My online demo is using this service.

Basically, what the lawmaking exercise is read the source, draw information technology in canvas, and salve it as data url with the sheet toDataURL function. Learn more well-nigh sheet here.

Now you tin bandy the real service with the fake one.

                      <            !            --            App.vue            --            >            ...            <            !            --            Javascript            --            >            <script>            // swap equally you need            import            {            upload            }            from            './file-upload.fake.service'            ;            // simulated service            // import { upload } from './file-upload.service';   // real service            <            /script>            ...                  

Done! Stop your backend API, refresh your browser, you should see our app is nevertheless working, calling simulated service instead.

Bonus: Delay Your Promises

Sometimes, you may want to delay the promises to see the country changes. In our case, the file upload may complete also fast. Let'southward write a helper role for that.

                      // utils.js            // utils to filibuster promise            function            wait            (            ms            )            {            return            (            10            )            =>            {            render            new            Promise            (            resolve            =>            setTimeout            (            (            )            =>            resolve            (10)            ,            ms)            )            ;            }            ;            }            export            {            expect            }                  

Then, you can use it in your component

                      <            !            --            App.vue            --            >            ...            <            !            --            Javascript            --            >            <script>            import            {            await            }            from            './utils'            ;            ...            save            (            formData            )            {            ...            .            upload            (formData)            .            and then            (            wait            (            1500            )            )            // DEV ONLY: wait for one.5s                        .            then            (            10            =>            {            this            .uploadedFiles            =            [            ]            .            concat            (x)            ;            this            .currentStatus            =            STATUS_SUCCESS            ;            }            )            ...            }            ,            <            /script>                  

Conclusion

That's information technology. This is how you can handle file upload without using any 3rd party libraries and plugins in Vue. It isn't that hard correct?

Happy coding!

The UI (Front-cease)

  • Sourcecode: https://github.com/chybie/file-upload-vue
  • Demo: https://vue-file-upload-1126b.firebaseapp.com/

The API (Dorsum-end) Tutorials and Sourcode

  • File upload with Hapi.js: https://scotch.io/bar-talk/handling-file-uploads-with-hapi-js, or
  • File upload with Express + Multer: https://scotch.io/tutorials/limited-file-uploads-with-multer, or
  • Switch to whatever cloud solution of your choice (Amazon S3, Google Bulldoze, etc).

osgoodfriess.blogspot.com

Source: https://www.digitalocean.com/community/tutorials/how-to-handle-file-uploads-in-vue-2

0 Response to "Wait to Upload Drag and Drop Images Before Submit"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel