VueJS upload image with additional data - vue.js

I am trying to upload image to the server and the same time to pass some additional data (in the same post request) using: VueJS 2 (CLI 3), axios, multer, sharp and I have NodeJS with MongoDB in the backend.
Front-end:
<form #submit.prevent="onSubmit" enctype="multipart/form-data">
<div class="input">
<label for="name">Name: </label>
<input
type="text"
id="name"
v-model="name">
</div>
<div class="input">
<label for="last_name">Your last_name: </label>
<input
type="text"
id="last_name"
v-model="last_name">
</div>
<div class="input">
<label for="permalink">permalink</label>
<input
type="text"
id="permalink"
v-model="permalink">
</div>
<div class="input">
<label for="price">price</label>
<input
type="text"
id="price"
v-model="price">
</div>
<div class="input">
<label for="photo">photo</label>
<input
style="display: none"
type="file"
id="photo"
#change="onFileSelected"
ref="fileInput">
<div #click="$refs.fileInput.click()">Pick file</div>
</div>
<div class="submit">
<md-button type="submit" class="md-primary md-raised">Submit</md-button>
</div>
</form>
VueJS methods:
import axios from 'axios'
export default {
data () {
return {
name: '',
last_name: '',
permalink: '',
selectedFile: null,
url: null,
price: 0,
second: false
}
},
methods: {
onFileSelected (event) {
this.selectedFile = event.target.files[0]
this.url = URL.createObjectURL(this.selectedFile)
},
onUpload () {
const fd = new FormData()
fd.append('image', this.selectedFile, this.selectedFile.name)
axios.post('http...', fd, {
onUploadProgress: uploadEvent => {
console.log('Upload Progress ' + Math.round(uploadEvent.loaded / uploadEvent.total * 100) + ' %')
}
})
.then(res => {
console.log(res)
})
},
onSubmit () {
const fd = new FormData()
fd.append('image', this.selectedFile, this.selectedFile.name)
fd.append('data', this.name, this.last_name)
axios.post('http://localhost:7000/api/create-user', fd, {
onUploadProgress: uploadEvent => {
console.log('Upload Progress ' + Math.round(uploadEvent.loaded / uploadEvent.total * 100) + ' %')
}
})
.then(res => {
console.log(res)
if (res.data === 'ok') {
this.second = true
}
})
.then(
setTimeout(function () {
this.second = false
this.reset()
}.bind(this), 2000)
)
.catch(error => console.log(error))
}
}
}
NodeJS:
controller.postCreateUser = (req, res) => {
const sharp = require('sharp');
const fs = require('fs');
const folderImg = 'backend/uploads/';
console.log(JSON.stringify(req.body));
console.log(req.file);
res.send("ok");
};
The results of req.file is (which is good):
{ fieldname: 'image',
originalname: 'musk.jpg',
encoding: '7bit',
mimetype: 'image/jpeg',
destination: 'backend/uploads/original/',
filename: 'musk-1545470459038.jpg',
path: 'backend\\uploads\\original\\musk-1545470459038.jpg',
size: 108787 }
The results of console.log(req.body) is
{"data":""}
The problem is here data has empty string and I don't receive any data. I need having the data to store to my database. How to do that?
If something isn't very clear to you, ask me for more details.

In your onSubmit method, you do this:
const fd = new FormData()
fd.append('image', this.selectedFile, this.selectedFile.name)
fd.append('data', this.name, this.last_name)
But FormData.append() expects these parameters:
name - The name of the field whose data is contained in value.
value - The field's value. This can be a USVString or Blob (including subclasses such as File).
filename Optional - The filename reported to the server (a USVString), when a Blob or File is passed as the second parameter.
The third parameter does not apply on this line: fd.append('data', this.name, this.last_name)
Instead, you can do either of these:
fd.append('data', `${this.name} ${this.last_name}`) // Send a single String
or
// Send both Strings separately
fd.append('name', this.name)
fd.append('last_name', this.last_name)
or
// Send the data as JSON
fd.append('data', JSON.stringify({name: this.name, last_name: this.last_name}))

Related

profilePic.move is not a function [AdonisJS]

I am facing the error while trying to upload a file(image) via the form on AdonisJS (I referred to the official docs #AdonisJS4.1 File Uploads)
await profilePic.move(Helpers.tmpPath('uploads'), {
name: 'custom.jpg',
overwrite: true
})
if (!profilePic.moved()) {
console.log('file not moved')
}
Official Docs Here
HTML
<form method="POST" action="upload" enctype="multipart/form-data">
<input type="file" name="profile_pic" />
<button type="submit"> Submit </button>
</form>
JS
const Helpers = use('Helpers')
Route.post('upload', async ({ request }) => {
const profilePic = request.file('profile_pic', {
types: ['image'],
size: '2mb'
})
await profilePic.move(Helpers.tmpPath('uploads'), {
name: 'custom-name.jpg',
overwrite: true
})
if (!profilePic.moved()) {
return profilePic.error()
}
return 'File moved'
})

Using SendGrid with Nuxt.js

This post is not a question actually.
More like help to the world for people who are struggling with Nuxt.js and SendGrid.
I've been using stackoverflow for such a long time so maybe now it's my turn to start helping others.
I've been working on Nuxt.js WebApp development for the past 8 weeks.
Nuxt.js is a massive learning curve and challenge for me but I really love working with this technology.
I spent the last 2 days developing sending the form with the use of SendGrid. There's not too much help online and I've been struggling a lot but I made it!
So maybe some people will find my post useful.
Here's my form:
<form
v-show="!isSubmitted"
class="contact-us__form"
#submit.prevent="validate">
<b-form-group :class="{'form-group--error': $v.name.$error}">
<b-form-input
id="name"
v-model.trim="$v.name.$model"
type="text"
placeholder="Full Name">
></b-form-input>
<div class="error" v-if="!$v.name.required">Field is required</div>
<div class="error" v-if="!$v.name.minLength">Name must have at least {{$v.name.$params.minLength.min}} letters.</div>
</b-form-group>
<b-form-group :class="{'form-group--error': $v.phone.$error}">
<b-form-input
id="phone"
v-model.trim="$v.phone.$model"
type="number"
placeholder="Phone Number">
></b-form-input>
<div class="error" v-if="!$v.phone.required">Field is required</div>
</b-form-group>
<b-form-group :class="{'form-group--error': $v.email.$error}">
<b-form-input
id="email"
v-model.trim="$v.email.$model"
type="email"
placeholder="Email Address">
></b-form-input>
<div class="error" v-if="!$v.email.required">Field is required</div>
</b-form-group>
<div class="d-flex align-items-end">
<b-form-group :class="{'form-group--error': $v.message.$error}">
<b-form-textarea
id="message"
v-model.trim="$v.message.$model"
type="text"
placeholder="Message"
></b-form-textarea>
<div class="error" v-if="!$v.message.required">Field is required</div>
<div class="error" v-if="!$v.message.minLength">Name must have at least {{$v.message.$params.minLength.min}} characters.</div>
</b-form-group>
<b-form-group>
<b-button
type="submit"
variant="secondary"
v-html="'S'"
:disabled="submitting" />
</b-form-group>
</div>
</form>
script:
export default {
mixins: [validationMixin],
components: {
subscribeBox
},
data() {
return {
map: bgMap,
name: "",
phone: "",
email: "",
message: "",
submitting: false,
isSubmitted: false,
error: false,
}
},
validations: {
name: {
required,
minLength: minLength(4)
},
phone: {
required,
},
email: {
required,
email
},
message: {
required,
minLength: minLength(5)
}
},
methods: {
validate() {
if (this.$v.$invalid || this.$v.$error|| this.submitting) {
this.$v.$touch();
return
}
this.onSsubmit();
},
async onSsubmit() {
this.submitting = true;
this.error = false;
try {
await this.$axios.$post('/api/v1/send-email', {
name: this.name,
phone: this.phone,
email: this.email,
message: this.message,
});
this.submitting = false
this.isSubmitted = true
await new Promise(resolve => setTimeout(resolve, 2500))
} catch(e) {
this.submitting = false
this.error = true
console.error(e)
}
},
},
}
nuxt.config.js
serverMiddleware: ['~/api/v1/send-email.js'],
api/v1/send-email.js (all the API Keys are placed in .env file)
const express = require("express");
const bodyParser = require('body-parser')
const sgMail = require('#sendgrid/mail');
const app = express();
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
app.use(bodyParser.json())
app.post("/", (req, res) => {
let msg = {
to: req.body.email, // Change to your recipient
from: '', // Change to your verified sender
subject: 'Message from ' + req.body.name,
text: 'telephone ' + req.body.phone + ', ' + 'message ' + req.body.message,
}
sgMail
.send(msg)
.then(() => {
return res.status(200).json({ 'message': 'Email sent!' })
})
.catch((error) => {
return res.status(400).json({ 'error': 'Opsss... Something went wrong ' + error })
})
});
module.exports = {
path: "/api/v1/send-email",
handler: app
};
This app is still not finished but the code is working 100%!
I'm new to Nuxt.js so some bits may not look awesome but I'm also happy and open to feedback and suggestions.
Thank you for reading my post and good luck with your project! :)

Submit form to Quip API with Axios

I want to submit a form data to Quip's Create Document API using vue.js and axios. This is what I've tried so far:
Form:
<form #submit="saveToQuip" method="POST" action="./post-order.html">
<div class="row">
<div class="col-lg-6 col-md-6">
<div class="row">
<div class="col-lg-12">
<div class="checkout__input">
<label>Receiver's Name<span>*</span></label>
<input type="text" name="First Name" v-model="fullname" required>
</div>
</div>
</div>
.....
</form>
JS:
new Vue({
el: '#submit-order',
data () {
return {
cartItems: [],
fullname: '',
facebookname: '',
email: 'NONE',
address: '',
phone_number: '',
payment_method: '',
delivery_dtime: '',
ordernotes: 'NONE'
}
},
methods:{
saveToQuip(submitEvent) {
......
axios.post('where-to-send-form-data', {
headers : {
Authorization: 'Bearer ' + personal_token,
'Content-Type': content_type
//Access-Control-Allow-Origin : *
},
params: {
title: this.fullname,
type: 'document',
member_ids: folder_id,
content: content
}
})
.then((response) => {
console.log(response)
})
.catch(function (error) {
console.log(error);
})
.then(function () {
}); ;
}
}
})
When I try to submit my form, it does not run the axios post command, and there is no error in the console. It just redirects the page to the next page. How do I achieve this?
Remove both method and action from your form and trigger the action from a button
<form #submit="saveToQuip" method="POST" action="./post-order.html">
to
<form>
...
<button #click="saveToQuip()">SAVE</button>

How To Correctly Bind Form in Vue & Vuex - Difference of v-model, :value & :value.sync

I'm fairly new to Vue & Vuex and am unsure on how to set up my form correctly.
<template>
<div>
<div v-if="error">{{error.message}}</div>
<form #submit.prevent>
<h1>Register</h1>
<input type="email" name="" id="" :value.sync="email" placeholder="Email">
<input type="password" :value.sync="password" placeholder="Password">
<button #click="signup">Submit</button>
Sign in
</form>
</div>
</template>
<script>
import {fireAuth} from '~/plugins/firebase.js'
export default {
data: () => ({
email: '',
password: '',
error: ''
}),
methods: {
signup() {
fireAuth.createUserWithEmailAndPassword(this.email, this.password).then(res => {
if (res) {
this.$store.commit('setCurrentUser', res.user)
this.$router.push('/')
}
}).catch(err => {
this.error = err
console.log(err)
})
}
}
}
</script>
At first I tried v-model to bind the form, it was throwing and error as I was mutating the store but I'm not storing email and password so I'm unsure how this is mutating the store? If I use :value or :value.sync then the email and password fields remain an empty string, so I'm not sure how to set up these correctly or how they differ from v-model
Edit: Adding my code from the store as requested
export const state = () => ({
currentUser: {}
})
export const mutations = {
setCurrentUser (state, obj) {
state.currentUser = obj
}
}
Vue.config.devtools = false
Vue.config.productionTip = false
const vm = new Vue({
el: "#app",
data() {
return {
email: '',
password: ''
}
},
methods: {
signup() {
console.log(this.email) // email value
console.log(this.password) // password value
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div id="app">
<div>
<form #submit.prevent="signup">
<h1>Register</h1>
<input type="email" v-model="email" placeholder="Email">
<input type="password" v-model="password" placeholder="Password">
<input type="submit">
</form>
</div>
</div>
</body>
</html>
I think I have found the solution, it was the way in which I was trying to use firebase. I have used v-model, but am using a firebase method onAuthStateChanged, only if that is called will I change the store. So in my firebase.js I added
const fireAuth = firebase.auth()
const fireDb = firebase.database()
export { fireAuth, fireDb }
export default async ({ store }) => {
await asyncOnAuthStateChanged(store)
}
function asyncOnAuthStateChanged (store) {
return new Promise(resolve => {
fireAuth.onAuthStateChanged(user => {
resolve(user)
store.commit('setCurrentUser', JSON.parse(JSON.stringify(user)))
})
})
}

How can I upload image to server using axios

I am trying to send a form along with an image. Note: I don't want to save the image in a database, I want to save it in a folder which I created on the server and just add a link of the image to the database.
From the server side I know how to handle this, but I don't know how to do this from font-end. With other words, how can I send the image using axios to the server.
<template>
<input type="text" class="form-control" id="specdesc" v-model="product.specdesc" name="specdesc" placeholder="Enter your name">
<input type="file" name="image" id="image" accept="image/*" >
<button type="submit" class="btn btn-sm btn-primary"#click.prevent="Submit()"> Submit</button>
</template>
<script>
export default {
name: 'Addproduct',
data(){
return{
image: '',
product:{
specdesc:'',
},
}
},
methods:{
Submit(){
var _this=this
// console.log(this.image)
console.log(this.selectedCategory.category_name)
var product = {
specification:this.product.specdesc,
imagename:this.image
}
this.$http.post('http://localhost:3000/api/companyproducts',product)
.then(function (response) {
console.log(response.data);
})
.catch(function (error) {
console.log("error.response");
});
}
},
}
</script>
Now my question is how can I upload a image as well as the input name using axios. Moreover I want to use the same method i.e var product to send.
A standard (mostly) approach will be to split the logic in two, if you want to save the image path on your product, you firstly need to upload the photo to the server and return their path.
pseudo example:
component's data
{
imagePath: '',
productSpect: ''
}
``
``html
<input type="text" v-model="productSpect" />
<input type="file" #change="uploadImage" name="image" id="image" accept="image/*" >
<button type="submit" #click.prevent="submit"> Submit</button>`
``
**uploadImage method**
uploadImage (e) {
let img = e.target.files[0]
let fd= new FormData()
fd.append('image', img)
axios.post('/upload-image', fd)
.then(resp => {
this.imagePath = resp.data.path
})
}
**submit method**
submit () {
let data = {
imagePath: this.imagePath,
productSpect: this.productSpect
}
axios.post('/path/to/save', data)
}
**edited method to handle just only 1 image on the server**
Change the input `#change` to just save the img on a property under data():
<input type="file" #change="image = e.target.file[0]" name="image" id="image" accept="image/*" >
submit() {
let fd= new FormData()
fd.append('image', this.image)
axios.post('/upload-image', fd)
.then(resp => {
this.imagePath = resp.data.path
let data = {
imagePath: this.imagePath,
productSpect: this.productSpect
}
axios.post('/path/to/save', data)
})
}
There is nothing specific to Vue in this question. To send a POST request with axios, the easiest thing to do is to grab the formData of the html form and pass it as the data argument to Axios. To do this in Vue, just give your form tag a ref, then create a formData from the form.
<form ref="myForm">
// then in your method...
var myFormData = new FormData(this.$refs.myForm)
axios({
method: 'post',
url: 'myurl',
data: myFormData,
config: { headers: {'Content-Type': 'multipart/form-data' }}
})
for upload file I've used vuetify https://vuetifyjs.com/en/components/file-inputs/
<v-file-input
accept="image/png, image/jpeg, image/bmp"
placeholder="Pick an avatar"
prepend-icon="mdi-camera"
v-model="imageData"
></v-file-input>
<v-btn color="primary" #click="onUpload">Upload</v-btn>
in my vue method I have
onUpload() {
let formData = new FormData();
formData.append('file', this.imageData);
axios.post(
"/images/v1/upload"
,formData
,{headers: {"Content-Type": "multipart/form-data"}}
)
.then(response => {
//...
})
.catch(e => {
//...
})
}
Best regards!