Table of Contents
URL: https://www.progressiverobot.com/vuejs-uploading-vue-picture-input/
File and image uploads are a must-have feature for many non-trivial apps. Lucky for us, it's easy to setup in Vue apps with vue-picture-input.
Check this post of ours if you're looking to create your own custom file selector input.
Installation
Install vue-picture-input via npm or Yarn:
$ npm install --save vue-picture-input
# or:
$ yarn add vue-picture-input
Usage
The vue-picture-input library allows for a variety of usages, and is very simple to use:
<template>
<picture-input
<!--Props go here-->
></picture-input>
</template>
Here are a few props that we're going to make use of:
- <^>ref<^>: We'll need this to access the base64 image string
- <^>width<^>, <^>height<^>: Dictate the width and height of the component
- <^>accept<^>: Used to restrict accepted file types
- <^>removable<^>: Specifies whether the component can be reset or not
Additionally, the component emits the following events:
- <^>removed<^>: emitted when picture is detached from component
- <^>changed<^>: emitted when the selected picture is changed (ie. reselected)
Here's what our code looks like for the file input:
[label index.vue]
<picture-input
ref="pictureInput"
@change="onChanged"
@remove="onRemoved"
:width="500"
:removable="true"
removeButtonClass="ui red button"
:height="500"
accept="image/jpeg, image/png, image/gif"
buttonClass="ui button primary"
:customStrings="{
upload: '<h1>Upload it!</h1>',
drag: 'Drag and drop your image here'}">
</picture-input>
—
Let's add a button that will allow us to upload the selected image to our backend.
It basically calls the <^>attemptUpload<^> method when clicked and has the disabled class binding. It'll only be enabled if there's a selected image:
<button @click="attemptUpload" v-bind:class="{ disabled: !image }">
Upload
</button>
Next, we'll use Axios to make network requests. We'll also leverage the Formdata API to create a sweet composition for posting image data:
[label upload.js]
import axios from 'axios';
export default function (url, file, name = 'avatar') {
if (typeof url !== 'string') {
throw new TypeError(`Expected a string, got ${typeof url}`);
}
// You can add checks to ensure the url is valid, if you wish
const formData = new FormData();
formData.append(name, file);
const config = {
headers: {
'content-type': 'multipart/form-data'
}
};
return axios.post(url, formData, config);
};
The above code accepts a url as the first parameter and a file object as the second parameter then returns a promise. It also sets the header's <^>content-type<^> to multipart/formdata so our backend API can tell what's in the pipes.
We can therefore use the above code as so:
import FormDataPost from '/upload';
//...
FormDataPost('http://localhost:8001/user/picture', image)
.then(response=>{
console.log("Uploaded picture successfully");
})
.catch(err=>{
console.error(err);
});
//...
Here's what our <^>onChanged<^>, <^>onRemoved<^> and <^>attemptUpload<^> methods look like:
//...
methods: {
onChanged() {
console.log("New picture loaded");
if (this.$refs.pictureInput.file) {
this.image = this.$refs.pictureInput.file;
} else {
console.log("Old browser. No support for Filereader API");
}
},
onRemoved() {
this.image = '';
},
attemptUpload() {
if (this.image){
FormDataPost('http://localhost:8001/user/picture', this.image)
.then(response=>{
if (response.data.success){
this.image = '';
console.log("Image uploaded successfully ✨");
}
})
.catch(err=>{
console.error(err);
});
}
}
}
//...
💪 Done! Our image upload is working on the frontend. Read-on for how to set this up on a Node.js backend using Express.