Vue Js Undefined on File Upload Event
Introduction
In this article, we will talk near how to handle file uploads with VueJs. We will create an images uploader that allow user to upload unmarried or multiple images file by drag and driblet or select file dialog.
We volition and then upload the selected images and display them accordingly. We will also learn to filter the upload file type, for instance, nosotros simply let images, do non allow file type like PDF.
- 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-end) and the API (back-terminate). Nosotros will be using VueJs to handle the UI part. We demand a backend application to accept the uploaded files. Y'all 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/handling-file-uploads-with-hapi-js, or
- File upload with Express + Multer: https://scotch.io/tutorials/limited-file-uploads-with-multer, or
- Switch to any deject solution of your choice (Amazon S3, Google Drive, etc).
Nosotros will be using File upload with Hapi.js as our backend throughout this manufactures. We will also acquire the tricks to enable simulated upload on the front end-end.
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 -g # then create project, with sass # follow the instructions to install all necessary dependencies vue init webpack-unproblematic file-upload-vue
Alright, all set. Let'south proceed to create our component.
File Upload Component
We will write our lawmaking in App.vue
. Remove all the auto-generated code in the file.
<!-- App.vue --> <!-- HTML Template --> <template > <div id = "app" > <div course = "container" > <!--UPLOAD--> <grade enctype = "multipart/form-data" novalidate v-if = "isInitial || isSaving" > <h1 > Upload images </h1 > <div course = "dropbox" > <input type = "file" multiple :name = "uploadFieldName" :disabled = "isSaving" @change = "filesChange($event.target.proper noun, $event.target.files); fileCount = $effect.target.files.length" accept = "image/*" class = "input-file" > <p five-if = "isInitial" > Drag your file(s) here to brainstorm <br > or click to browse </p > <p 5-if = "isSaving" > Uploading {{ fileCount }} files... </p > </div > </form > </div > </template > <!-- Javascript --> <script > </script > <!-- SASS styling --> <fashion lang = "scss" > </style >
Notes:-
- Our
App.vue
component consists of 3 part: template (HTML), script (Javascript) and styles (SASS). - Our template has an upload course.
- The form aspect
enctype="multipart/grade-data"
is important. To enable file upload, this attribute must exist set. Larn more than nearly enctype hither. - We have a file input
<input blazon="file" />
to accept file upload. The belongingsmultiple
betoken it's permit multiple file upload. Remove information technology for unmarried file upload. - We will handle the file input
change
event. Whenever the file input change (someone drib or select files), we will trigger thefilesChange
role and pass in the command proper name and selected files$event.target.files
, and then upload to server. - We limit the file input to accept images only with the attribute
accept="image/*"
. - The file input volition exist disabled during upload, so user tin only drop / select files once more afterwards upload complete.
- Nosotros capture the
fileCount
of the when file changes. Nosotros utilize thefileCount
variable in displaying number of files uploadingUploading {{ fileCount }} files...
.
Style our File Upload Component
Now, that's the interesting part. Currently, our component wait like this:
Nosotros need to transform it to look like this:
Let'due south mode it!
<!-- App.vue --> ... <!-- SASS styling --> <style lang="scss"> .dropbox { outline : 2px dashed greyness; / * the dash box * / outline-kickoff : -10px; background : lightcyan; color : dimgray; padding : 10px 10px; min-acme : 200px; / * minimum tiptop * / position : relative; cursor : pointer; } .input-file { opacity : 0; / * invisible but it's there! * / width : 100%; height : 200px; position : accented; cursor : pointer; } .dropbox : hover { background : lightblue; / * when mouse over to the drop zone, change color * / } .dropbox p { font-size : one.2em; text-align : center; padding : 50px 0; } </manner>
With only few lines of scss, our component looks prettier now.
Notes:-
- We make the file input invisible by applying
opacity: 0
style. This doesn't hide the file input, information technology just make it invisible. - Then, we style the file input parent element, the
dropbox
css grade. We get in wait like a drop file zone surround with dash. - Then, we align the text within dropbox to center.
File Upload Component Code
Let'south proceed to code our component.
< ! -- App.vue -- > ... < ! -- Javascript -- > <script> import { upload } from './file-upload.service' ; const STATUS_INITIAL = 0 , STATUS_SAVING = ane , STATUS_SUCCESS = 2 , STATUS_FAILED = 3 ; consign default { proper noun : 'app' , information ( ) { return { uploadedFiles : [ ] , uploadError : null , currentStatus : null , uploadFieldName : 'photos' } } , computed : { isInitial ( ) { return this .currentStatus === STATUS_INITIAL ; } , isSaving ( ) { render this .currentStatus === STATUS_SAVING ; } , isSuccess ( ) { return this .currentStatus === STATUS_SUCCESS ; } , isFailed ( ) { return this .currentStatus === STATUS_FAILED ; } } , methods : { reset ( ) { // reset course to initial state this .currentStatus = STATUS_INITIAL ; this .uploadedFiles = [ ] ; this .uploadError = naught ; } , save ( formData ) { // upload data to the server this .currentStatus = STATUS_SAVING ; upload (formData) . and so ( 10 => { 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 ; // suspend the files to FormData Array . from ( Assortment (fileList.length) . keys ( ) ) . map ( 10 => { formData. append (fieldName, fileList[x] , fileList[x] .name) ; } ) ; // relieve it this . salvage (formData) ; } } , mounted ( ) { this . reset ( ) ; } , } < /script>
Notes:-
- Our component will have a few statuses: STATUS_INITIAL, STATUS_SAVING, STATUS_SUCCESS, STATUS_FAILED, the variable name is pretty expressive themselves.
- Later, we will telephone call the Hapi.js file upload API to upload images, the API accept a field call
photos
. That'due south our file input field name. - We handle the file changes with the
filesChange
function.FileList
is an object returned past the files property of the HTML <input> element. It let us to access the list of files selected with the <input type="file"> element. Learn more [here]((https://programmer.mozilla.org/en/docs/Web/API/FileList). - We then create a new
FormData
, and append all ourphotos
files to it.FormData
interface provides a mode to easily construct a prepare of cardinal/value pairs representing form fields and their values. Learn more here. - The
salve
role will call our file upload service (hang on, nosotros volition create the service next!). Nosotros also set the status according to the event. -
mount()
is the vue component life cycle hook. During that point, we will set our component condition to initial land.
File Upload Service
Let's continue to create our service. We will exist using axios to brand 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 ` ; return axios. post (url, formData) // get data . then ( x => x.data) // add url field . then ( 10 => x. map ( img => Object. assign ( { } , img, { url : ` ${ BASE_URL } /images/ ${img.id} ` } ) ) ) ; } export { upload }
Nothing much, the code is pretty expressive itself. We upload the files, expect for the issue, map it accordingly.
You may run the application at present with npm run dev
command. Try uploading a couple of images, and information technology'south working! (Remember to starting time your backend server)
Display Success and Failed Upshot
We can upload the files successfully at present. Nevertheless, in that location'south no indication in UI. Let's update our HTML template.
<!-- App.vue --> <!-- HTML Template --> <template > <div id = "app" > <div form = "container" > ...form... <!--SUCCESS--> <div v-if = "isSuccess" > <h2 > Uploaded {{ uploadedFiles.length }} file(due south) successfully. </h2 > <p > <a href = "javascript:void(0)" @click = "reset()" > Upload again </a > </p > <ul class = "list-unstyled" > <li five-for = "item in uploadedFiles" > <img :src = "item.url" class = "img-responsive img-thumbnail" :alt = "item.originalName" > </li > </ul > </div > <!--FAILED--> <div v-if = "isFailed" > <h2 > Uploaded failed. </h2 > <p > <a href = "javascript:void(0)" @click = "reset()" > Effort once more </a > </p > <pre > {{ uploadError }} </pre > </div > </div > </div > </template >
Notes:-
- Display the uploaded paradigm when upload successfully.
- Display the error message when upload failed.
Fake the Upload in Forepart-end
If y'all are lazy to start the back-cease awarding (Hapi, Express, etc) to handle file upload. Here is a simulated service to supplant the file upload service.
// file-upload.false.service.js role upload ( formData ) { const photos = formData. getAll ( 'photos' ) ; const promises = photos. map ( ( 10 ) => getImage (x) . then ( img => ( { id : img, originalName : x.proper noun, fileName : x.name, url : img } ) ) ) ; return Promise. all (promises) ; } function getImage ( file ) { return new Promise ( ( resolve, reject ) => { const fReader = new FileReader ( ) ; const img = document. createElement ( 'img' ) ; fReader. onload = ( ) => { img.src = fReader.result; resolve ( getBase64Image (img) ) ; } fReader. readAsDataURL (file) ; } ) } function getBase64Image ( img ) { const canvas = certificate. createElement ( 'canvas' ) ; canvas.width = img.width; sail.height = img.peak; const ctx = sheet. getContext ( 'second' ) ; ctx. drawImage (img, 0 , 0 ) ; const dataURL = canvas. toDataURL ( 'image/png' ) ; render dataURL; } export { upload }
Came across this solution in this Stackoverflow postal service. Pretty useful. My online demo is using this service.
Basically, what the lawmaking practise is read the source, draw it in canvas, and save information technology every bit data url with the canvas toDataURL
function. Learn more about canvas here.
Now you tin can bandy the real service with the fake one.
< ! -- App.vue -- > ... < ! -- Javascript -- > <script> // swap as you need import { upload } from './file-upload.simulated.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 still working, calling fake service instead.
Bonus: Delay Your Promises
Sometimes, you may desire to delay the promises to see the state changes. In our case, the file upload may complete too fast. Let'southward write a helper function for that.
// utils.js // utils to delay hope function look ( ms ) { return ( x ) => { return new Promise ( resolve => setTimeout ( ( ) => resolve (ten) , ms) ) ; } ; } export { wait }
Then, you can utilise it in your component
< ! -- App.vue -- > ... < ! -- Javascript -- > <script> import { wait } from './utils' ; ... save ( formData ) { ... . upload (formData) . then ( wait ( 1500 ) ) // DEV ONLY: wait for 1.5s . then ( x => { this .uploadedFiles = [ ] . concat (x) ; this .currentStatus = STATUS_SUCCESS ; } ) ... } , < /script>
Conclusion
That's it. This is how you lot tin handle file upload without using whatever tertiary party libraries and plugins in Vue. Information technology isn't that hard right?
Happy coding!
The UI (Forepart-cease)
- Sourcecode: https://github.com/chybie/file-upload-vue
- Demo: https://vue-file-upload-1126b.firebaseapp.com/
The API (Back-cease) Tutorials and Sourcode
- File upload with Hapi.js: https://scotch.io/bar-talk/handling-file-uploads-with-hapi-js, or
- File upload with Limited + Multer: https://scotch.io/tutorials/express-file-uploads-with-multer, or
- Switch to whatever cloud solution of your choice (Amazon S3, Google Bulldoze, etc).
russellpossiounds58.blogspot.com
Source: https://www.digitalocean.com/community/tutorials/how-to-handle-file-uploads-in-vue-2
0 Response to "Vue Js Undefined on File Upload Event"
Post a Comment