axios sending null when uploading files to laravel api. Error Message: "Call to a member function store() on null" - vue.js

I have a form with file upload. I am using vue, axios and laravel 7 at the moment.
My form is as given below:
<form v-on:submit.prevent="createTask" enctype="multipart/form-data" method="post">
<div class="form-group">
<label for="title">Title</label>
<input type="text" class="form-control" name="title" id="title" v-model="task.title" />
<label for="content">Content</label>
<textarea v-model="task.content" class="form-control" id="content" name="content" rows="4" ></textarea>
<button type="submit" class="btn btn-sm btn-primary
pull-right" style="margin:10px; padding:5 15 5 15; background-color:#4267b2">Save Task</button>
<span>
<button #click="removeImage" class="btn btn-sm btn-outline-danger" style="margin:10px; padding:5 15 5 15; ">Remove File</button>
</span>
<input type="file" id="image" name="image" />
</div>
</form>
Axios submission
createTask() {
console.log(document.getElementById('image').files[0])
axios({
headers: { 'Content-Type': 'multipart/form-data' },
method: 'post',
url: '/api/tasks',
data: {
task_id : this.task.id,
title : this.task.title,
content : this.task.content,
created_by : this.$userId,
image : document.getElementById('image').files[0],
}
})
.then(res => {
// check if the request is successful
this.$emit("task-created");
this.task = {}
})
.catch(function (error){
console.log(error)
});
}
My controller code
public function store(Request $request)
{
$task = $request->isMethod('put') ? Task::findOrFail($request->task_id) : New Task;
$task->id = $request->input('task_id');
$task->title = $request->input('title');
$task->content = $request->input('content');
$task->views = $request->input('views');
$task->created_by = $request->input('created_by');
$task->image = $request->image->store('images');
//$task->image = $request->file('image')->store('images');
if($task->save()) {
return new TaskResource($task);
}
}
The request payload
{task_id: "", title: "test", content: "test content", created_by: "1", image: {}}
We can see the image attribute is empty
The response error message
"Call to a member function store() on null"
The output of console.log(document.getElementById('image').files[0])
File {name: "test.jpg", lastModified: 1590920737890, lastModifiedDate: Sun May 31 2020 11:25:37 GMT+0100 (British Summer Time), webkitRelativePath: "", size: 436632, …}
lastModified: 1590920737890
lastModifiedDate: Sun May 31 2020 11:25:37 GMT+0100 (British Summer Time) {}
name: "test.jpg"
size: 436632
type: "image/jpeg"
webkitRelativePath: ""
proto: File

I solved this problem by changing the axios post request as follows. I used FormData to append and send data to server
createTask() {
let data = new FormData;
data.append('image', document.getElementById('image').files[0]);
data.append('task_id', this.task.id);
data.append('title', this.task.title);
data.append('content', this.task.content);
data.append('created_by', this.$userId);
axios({
headers: { 'Content-Type': 'multipart/form-data' },
method: 'post',
url: '/api/tasks',
data: data
})
.then(res => {
// check if the request is successful
this.$emit("task-created");
this.image = '';
this.task = {}
})
.catch(function (error){
console.log(error)
});
}

Assuming the image column has the right structure, you could try with somenthing like this:
<?php
public function store(Request $request)
{
$task = $request->isMethod('put') ? Task::findOrFail($request->task_id) : New Task;
$task->id = $request->input('task_id');
$task->title = $request->input('title');
$task->content = $request->input('content');
$task->views = $request->input('views');
$task->created_by = $request->input('created_by');
$task->image = $request->hasFile('images')
? $request->file('images')
: null;
if($task->save()) {
return new TaskResource($task);
}
}
laravel 5.4 upload image read here for more information

Related

VueJS upload image with additional data

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}))

React Router: login post

I'm a beginner with React & Router and I'm trying to set up a very simple login form & redirection.
I don't really understand where i have to put my 'logic code' (an ajax call and a redirection).
This is what I get when I try to login..
GET security/login?callback=jQuery33102958950754760552_1525660032193&format=json&_=1525660032194 40 5()
It should be "POST" not "GET"
Here is what I've write.
import React from "react";
import { Link } from "react-router-dom";
import $ from "jquery";
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
userid: "",
password: "",
submitted: false
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(e) {
e.preventDefault();
var root = 'security/login';
//var userid = $("#userid").val();
// var password = $("#password").val();
var formData = {
"userId" : $('input[name=userid]').val(),//$('input[name=userid]').val()
"password" : $('input[name=password]').val()//$('input[name=password]').val()
};
var jsondata = JSON.stringify(formData);
console.log(jsondata);
alert("test" +jsondata);
$.ajax({
url: root,
method: 'POST',
data: {
format: 'json'
},
contentType : "application/json; charset=utf-8",
error: function() {
$('#info').html('<p>An error has occurred</p>');
},
headers: {
'Content-Type': 'application/json', /*or whatever type is relevant */
'Accept': 'application/json' /* ditto */
},
dataType: 'jsonp',
encode : true,
success: function(data, response) {
alert(+data.status.message);
var $title = $('<h1>').text(data.talks[0].talk_title);
var $description = $('<p>').text(data.talks[0].talk_description);
$('#info')
.append($title)
.append($description);
}
});
//done(Login.function(data)
// {
// this.setState({});
// console.log(this.state.data);
// }
}
render() {
// const { loggingIn } = this.props;
// const { userid, password, submitted } = this.state;
return (
<div className="container">
<div className="col-md-5 col-md-offset-13 login-form text-center">
<div className="form-top">
<div className="form-top-left">
<h3>LOGIN PAGE</h3>
<p>Please enter your userID and password</p>
</div>
</div>
<form onSubmit={this.handleSubmit}>
<div className="input-group col-lg-10 col-md-offset-1">
<span className="input-group-addon">
<i className="glyphicon glyphicon-user" />
</span>
<input
className="form-control"
placeholder="UserID"
name="userid"
id="userid"
type="text"
required
/>
</div>
<div className="input-group col-lg-10 col-md-offset-1">
<span className="input-group-addon">
<i className="glyphicon glyphicon-lock" />
</span>
<input
type="password"
name="password"
id="password"
className="form-control"
placeholder="Password"
required
/>
</div>
<button type="submit"
className="btn btn-danger btn-block col-xs-6 col-lg-11"
id="login">
>
LOGIN
</button>
</form>
<div className="form-footer">
<div className="row">
<div className="col-xs-7 blink">
<i className="fa fa-unlock-alt" />
</div>
</div>
</div>
</div>
</div>
);
}
}
export default Login;
Hope you all can help me... Thanks in advance

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!

Performing action when clicking off an element

The below code allows me to have a click-to-edit header tag within my application.
I'm looking for the best way to handle exiting editing mode when any other action is performed on the page... either a click or a drag-n-drop.
<validator name="teamSetValidation">
<input id='teamSetName' v-if="isEditingName" type="text" v-model="teamSet.name" class="semi-bold p-t-10 p-b-10 m-l-15 edit-header" v-on:keyup.enter="saveTeamSetName()" v-on:keyup.esc="doneEditing()" v-validate:name.required.maxlength="teamSetRules" :isEditingName="true"/>
<h3 v-else class="semi-bold p-t-10 p-b-10 m-l-15" v-on:click="editing()" :isEditingName="false">{{ teamSet.name }} <span class="fa fa-edit"></span></h3>
<div class="text-small">
<span class="text-danger" v-if="$teamSetValidation.teamSet.name.required">A name is required.</span>
<span class="text-danger" v-if="$teamSetValidation.teamSet.name.maxlength">The name you provided is too long.</span>
</div>
<div class="b-grey b-b m-t-10"></div>
</validator>
Javascript:
var vm = new Vue({
el: '#page',
data: {
// When true, user can edit the teamSet name
isEditingName: false,
teamSet: teamSet,
teamSetRules: {
required: false,
maxlength: 64
}
},
methods: {
editTeamSetName: function () {
alert('editing');
},
saveTeamSetName: function () {
if(this.$teamSetValidation.valid) {
this.doneEditing();
var teamSet = this.teamSet,
self = this;
$.ajax({
url: '/team/'+teamSet.id,
type: 'PATCH',
data: {
'name': teamSet.name
},
error: function(res) {
Messenger().post({
message: 'Unable to save changes',
type: 'error',
hideAfter: 3
});
self.editing();
}
});
}
},
editing: function () {
this.isEditingName = true;
Vue.nextTick(function () {
$('#teamSetName').focus();
});
},
doneEditing: function () {
this.isEditingName = false;
}
}
});
Attaching a blur event to the input should do the trick:
<input id='teamSetName' v-if="isEditingName"
type="text" v-model="teamSet.name"
class="semi-bold p-t-10 p-b-10 m-l-15 edit-header"
v-on:keyup.enter="saveTeamSetName()"
v-on:keyup.esc="doneEditing()"
v-validate:name.required.maxlength="teamSetRules"
:isEditingName="true" v-on:blur="doneEditing()"
/>

Switching Google reCaptcha Version 1 from 2

I have successfully designed and implemented Google reCaptcha Version 2 but now my Manager wants that to be of version 1 with numbers to be entered and validated. Is there a way to switch from later to former i.e.- from 2 to 1. I am using following library for reCaptcha:
<script src='https://www.google.com/recaptcha/api.js'></script>
Update..
To implement Captcha inside form i am using following HTML..
<form class="contact_form" action="#" method="post" name="contact_form">
<div class="frm_row">
<label id="lblmsg" />
<div class="clear">
</div>
</div>
<div class="g-recaptcha" data-sitekey="6Lduiw8TAAAAAOZRYAWFUHgFw9_ny5K4-Ti94cY9"></div>
<div class="login-b">
<span class="button-l">
<input type="button" id="Captcha" name="Submit" value="Submit" />
</span>
<div class="clear"> </div>
</div>
</form>
As i need to get the Captcha inside the above form to Validate and get the response on button click but as now i am using <script src="http://www.google.com/recaptcha/api/challenge?k=6Lduiw8TAAAAAOZRYAWFUHgFw9_ny5K4-Ti94cY9"></script> , so not getting the Captcha inside the form ..Please help me to get that ..Also here is the Jquery Ajax code to send the request on Server side code..
$(document).ready(function () {
alert("hii1");
$('#Captcha').click(function () {
alert("Hii2");
if ($("#g-recaptcha-response").val()) {
alert("Hii3");
var responseValue = $("#g-recaptcha-response").val();
alert(responseValue);
$.ajax({
type: 'POST',
url: 'http://localhost:64132/ValidateCaptcha',
data: JSON.stringify({ "CaptchaResponse": responseValue }),
contentType: "application/json; charset=utf-8",
dataType: 'json', // Set response datatype as JSON
success: function (data) {
console.log(data);
if (data = true) {
$("#lblmsg").text("Validation Success!!");
} else {
$("#lblmsg").text("Oops!! Validation Failed!! Please Try Again");
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("Error");
}
});
}
});
});
Please help me ..Thanks..
You have to verify the reCaptcha at "http://www.google.com/recaptcha/api/verify" on Server side.
The parameters of this are:
privatekey: Your Private Key
remoteip: User's IP Address
challenge: Value of input[name=recaptcha_response_field]
response: Value of input[name=recaptcha_challenge_field]
Therefore, you have to post them on your server-side method like this:
cshtml file:
var recaptchaResponseField=$("input[name=recaptcha_response_field]").val();
var recaptchaChallengeField=$("input[name=recaptcha_challenge_field]").val();
// ajax block
$.ajax({
url: '/api/VerifyReCaptcha/', // your Server-side method
type: 'POST',
data: {
ipAddress: '#Request.ServerVariables["REMOTE_ADDR"]',
challengeField: recaptchaChallengeField,
responseField: recaptchaResponseField
},
dataType: 'text',
success: function (data) {
// Do something
},
Since you are using .NET so an example of C# code is as follows:
cs file:
using System.Net;
using System.Collections.Specialized;
[HttpPost]
public bool VerifyReCaptcha(string ipAddress, string challengeField, string responseField)
{
string result = "";
using (WebClient client = new WebClient())
{
byte[] response =
client.UploadValues("http://www.google.com/recaptcha/api/verify", new NameValueCollection()
{
{ "privatekey", "{Your private key}" },
{ "remoteip", ipAddress },
{ "challenge", challengeField },
{ "response", responseField },
});
result = System.Text.Encoding.UTF8.GetString(response);
}
return result.StartsWith("true");
}