Axios post request error when sending an array with an image - laravel

I have a form that has an array input and an image, when sending the request without the image it's getting stored correctly in the database, when the image input and the "Content-Type": "multipart/form-data" header both added, it send the request with the correct data but it outputs that the array data is invalid.
the code is written in Vue 3 and the backend is a laravel api.
<script>
import axios from "axios";
export default {
name: "AddPayloadModel",
data() {
return {
drone_id: [],
drone_models: [],
image: null,
previewImage: null,
};
},
created() {
this.getDroneModels();
},
methods: {
getDroneModels() {
axios.get("http://127.0.0.1:8000/api/drone-models").then((response) => {
this.drone_models = response.data;
});
},
imgUpload(e) {
this.image = e.target.files[0];
const reader = new FileReader();
reader.readAsDataURL(this.image);
reader.onload = (e) => {
this.previewImage = e.target.result;
};
},
submit(e) {
const form = new FormData(e.target);
const inputs = Object.fromEntries(form.entries());
inputs.drone_id = Object.values(this.drone_id);
console.log(inputs.drone_id);
axios
.post("http://127.0.0.1:8000/api/payload-model/add", inputs, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((res) => {
if (res.data.isUnattachableDrones) {
console.log(res);
alert(res.data.unattachableDrones);
} else {
this.$router.push("/home");
}
})
.catch((e) => {
console.error(e);
});
},
},
};
</script>
<template>
<main class="form-signin">
<form #submit.prevent="submit">
<h1 class="h3 mb-3 fw-normal">Add Payload Model</h1>
<div class="form-floating">
<input
class="form-control"
autocomplete="off"
name="brand_name"
placeholder="Brand Name"
/>
<label>Brand Name</label>
</div>
<div class="form-floating">
<input
class="form-control"
autocomplete="off"
name="model_name"
placeholder="Model name"
/>
<label>Model Name</label>
</div>
<div class="form-floating">
<input
class="form-control"
autocomplete="off"
name="type"
placeholder="Type"
/>
<label>Type</label>
</div>
<select
v-model="drone_id"
multiple="multiple"
name="drone_id"
style="height: auto"
class="form-select last-input"
>
<template v-for="drone in drone_models">
<option :value="drone.id">
{{ drone.brand_name }}
</option>
</template>
</select>
<input
name="image"
ref="fileInput"
accept="image/*"
type="file"
#input="imgUpload"
/>
<button class="w-100 btn btn-lg btn-form" type="submit">Submit</button>
</form>
</main>
</template>
here is the response when submitting the form.
and here is the payload.

It got solved by changing the way to push data to the post request to a formData and append it manually like this.
submit() {
const formData = new FormData();
formData.append(
"brand_name",
document.getElementById("brand_name").value
);
formData.append(
"model_name",
document.getElementById("model_name").value
);
formData.append("type", document.getElementById("type").value);
formData.append("image", document.getElementById("image").files[0]);
this.drone_id.forEach((drone_id) => {
formData.append("drone_id[]", drone_id);
});
const headers = { "Content-Type": "multipart/form-data" };
axios
.post("http://127.0.0.1:8000/api/payload-model/add", formData, headers)
.then((res) => {
console.log(res);
if (res.data.isUnattachableDrones) {
console.log(res);
alert(res.data.unattachableDrones);
} else {
this.$router.push("/home");
}
})
.catch((e) => {
console.error(e);
});
},

Related

How get quill and other form to JS submit function?

I wanna use quill rich editor and other fields on my form. But cant get access to quill innerHTML from JS function. I am using Laravel with Alpinejs and my code is
<form x-data="contactForm()" #submit.prevent="submit">
<div class="col-12">
<div class="mt-2 w-100 bg-white" wire:ignore>
<div
x-data
x-ref="quillEditor"
x-init="
quill = new Quill($refs.quillEditor, {
theme: 'snow',
modules: {toolbar: '#toolbar'}
});
quill.on('text-change', function () {
$dispatch('input', quill.root.innerHTML);
});"
wire:model.debounce.2000ms="description"
class="sign__textarea"
style="height: 300px;"
>{{ old('description', $services_users->description) }}
</div>
</div>
</div>
<div class="col-12">
<input name="message" x-model="data.title">
</div>
<div class="col-12 col-xl-3 mt-5">
<button type="submit" x-text="buttonText" :disabled="loading"></button>
</div>
</form>
<script>
function contactForm() {
return {
data: {
title: "",
myQuill: quill.root.innerHTML,
},
buttonText: "Save",
loading: false,
submit() {
this.buttonText = "Saving...";
this.loading = true;
fetch('myurl.endpoint', {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
},
body: JSON.stringify(this.data),
}).then(() => {
alert("Form submitted");
}).catch(() => {
alert("Something went wrong");
}).finally(() => {
this.data.title = ""
this.buttonText = "Save";
this.loading = false;
});
},
};
}
</scirpt>
Now i have an error Can't find variable: quill how can i get all fields from form and send to backend event if quill is not a form field?
It doesn't work because you're calling the variable "quill" in the parent and you're declaring it in the child. To fix it declare the x-init directive in the form.
listening to the "text-change" event is not necessary. A good option is to add the content of the container before submitting the form.
see : https://alpinejs.dev/directives/data#scope
<form x-data="contactForm()" x-init="
quill = new Quill($refs.quillEditor, {
theme: 'snow'
});
quill.on('text-change', function () {
$dispatch('input', quill.root.innerHTML);
});" #submit.prevent="submit">
<div class="col-12">
<div class="mt-2 w-100 bg-white" wire:ignore>
<div
x-ref="quillEditor"
wire:model.debounce.2000ms="description"
class="sign__textarea"
style="height: 300px;"
>{{ old('description', $services_users->description) }}
</div>
</div>
</div>
<div class="col-12">
<input name="message" x-model="data.title">
</div>
<div class="col-12 col-xl-3 mt-5">
<button type="submit" x-text="buttonText" :disabled="loading"></button>
</div>
</form>
<script>
function contactForm() {
return {
quill:null,
data: {
title: "",
// myQuill: function(){ return this.quill.root.innerHTML}
},
buttonText: "Save",
loading: false,
submit() {
this.buttonText = "Saving...";
//add content quill here
this.data.myQuill = this.quill.root.innerHTML;
this.loading = true;
fetch('myurl.endpoint', {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
},
body: JSON.stringify(this.data),
}).then(() => {
alert("Form submitted");
}).catch(() => {
alert("Something went wrong");
}).finally(() => {
this.data.title = ""
this.buttonText = "Save";
this.loading = false;
});
},
};
}
</script>
Ok this is what i did
<form x-data="contactForm()" x-init="initQuill()" x-on:submit="submit()" method="POST" action="target.url">
<div x-ref="editor"></div>
<input x-ref="editorValue" type="hidden" name="hidden_input">
<button>Save</button>
</form>
<script>
function contactForm(){
return {
initQuill(){
new Quill(this.$refs. editor, {theme: 'snow'});
},
submit(){
console.log(this.$refs. editor.__quill.root.innerHTML);
this.$refs.editorValue.value = this.$refs.editor.__quill.root.innerHTML;
}
}
}
</script>
Now is ok and works with basic. You can extend function with new features etc. Thx for help guys.

How to fix Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'status') at app.js

I had the following code and everything was working as expected.
<template>
<div id="login">
<form #submit.prevent="submit">
<p class="loading" :class="{ hideLoad: !loading }">Loading...</p>
<label for="email">Email</label>
<input type="text" id="email" v-model="fields.email" />
<span v-if="errors && errors.email" class="error">{{
errors.email[0]
}}</span>
<label for="password">Password</label>
<input type="password" id="password" v-model="fields.password" />
<span v-if="errors && errors.password" class="error">{{
errors.password[0]
}}</span>
<button type="submit">Log In</button>
<span
>Don't have an account?
<router-link :to="{ name: 'Register' }">Sign Up</router-link></span
>
</form>
</div>
</template>
<script>
export default {
data() {
return {
fields: {},
errors: {},
loading: false,
};
},
methods: {
submit() {
this.loading = true;
axios
.post("/api/login", this.fields)
.then((res) => {
if (res.status == 201) {
localStorage.setItem("authenticated", "true");
this.$router.push({ name: "Dashboard" });
}
})
.catch((error) => {
if (error.response.status == 422) {
this.errors = error.response.data.errors;
this.loading = false;
}
});
},
},
};
</script>
I then decided to use pinia to manage state and came up with the following.
<template>
<div id="login">
<form #submit.prevent="loginStore.submit">
<p class="loading" :class="{ hideLoad: !loginStore.loading }">Loading...</p>
<label for="email">Email</label>
<input type="text" id="email" v-model="loginStore.fields.email" />
<span v-if="loginStore.errors && loginStore.errors.email" class="error">{{
loginStore.errors.email[0]
}}</span>
<label for="password">Password</label>
<input type="password" id="password" v-model="loginStore.fields.password" />
<span v-if="loginStore.errors && loginStore.errors.password" class="error">{{
loginStore.errors.password[0]
}}</span>
<button type="submit">Log In 2</button>
<span
>Don't have an account?
<router-link :to="{ name: 'Register' }">Sign Up</router-link></span
>
</form>
</div>
</template>
<script setup>
import {useLoginStore} from '../stores/login'
const loginStore = useLoginStore();
</script>
My login.js
import { defineStore } from 'pinia'
export const useLoginStore = defineStore('login', {
state: () => {
return {
fields: {},
errors: {},
loading: false,
}
},
actions: {
submit() {
this.loading = true;
axios
.post("/api/login", this.fields)
.then((res) => {
if (res.status == 201) {
localStorage.setItem("authenticated", "true");
this.$router.push({ name: "Dashboard" });
}
})
.catch((error) => {
if (error.response.status == 422) {
this.errors = error.response.data.errors;
this.loading = false;
}
});
},
},
})
Everything is working as before and the only error is that when I fill the form with the correct credential and submit then I get Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'status') at app.js
I've tried all I can and I just can't figure out what it is that I'm doing wrong.
After debugging it further, I realized the error was coming from this.$routerinstance since a user gets login successfully and the status code is okay when I console log.
The instance is available in components only https://router.vuejs.org/api/#component-injected-properties
So, I fixed the problem by importing router in the store.js file,
import router from '../router'
then I pushed to another route by just using
router.push({ name: "Dashboard" });
instead of this.$router.push({ name: "Dashboard" });

Laravel API: "POST http://localhost/api/post 419 (unknown status)" (Vue.js)

Im trying to make a post in a vue component with Laravel Api.
I got CSRF token in my welcome.blade.php:
<meta name="csrf-token" content="{{ csrf_token() }}">
Page does not refresh or add anything when i click on the button.
If i click on the button i get this in my console:
POST http://localhost/api/post 419 (unknown status)
PostList.vue
<template>
<div class="container py-4">
<form enctype="multipart/form-data" method="post" action="" #submit.prevent="addPost">
<input type="hidden" name="_token" value=""/>
<div class="modal-header">
<h4 class="modal-title">Create Post</h4>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
</div>
<div class="modal-body">
<div class="form-group">
<label>Title</label>
<input type="text" class="form-control" placeholder="Title" v-model="post.title">
</div>
<div class="form-group">
<label>Description</label>
<textarea class="form-control" placeholder="Body" v-model="post.body"></textarea>
</div>
</div>
<div class="modal-footer">
<input type="button" class="btn btn-default" data-dismiss="modal" value="Cancel">
<input type="submit" class="btn btn-primary" value="Add">
</div>
</form>
</div>
</template>
<script>
export default {
data() {
return {
post: {
id: '',
title: '',
body: ''
}
};
},
created() {
this.getPosts();
},
methods: {
addPost(){
fetch('api/post', {
method: 'post',
body: JSON.stringify(this.post),
headers: {
'content-type': 'apllication/json'
}
})
.then(response => response.json())
.then(data => {
this.getPosts();
})
.catch(err => console.log(err));
}
}
};
</script>
PostController.php
public function store_vue(Request $request){
$post = new Posts();
$post->title = $request->get('title');
$post->body = $request->get('body');
$post->slug = Str::slug($post->title);
$post->author_id = $request->user()->id;
if ($post->save()) {
return new PostResource($post);
}
}
You are getting a 419 error because the request is missing the CSRF token.
You can add it to your form and see if it works for you
<form enctype="multipart/form-data" method="post" action="" #submit.prevent="addPost">
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
OR
Add the header with the CSRF to your call
<script>
export default {
data() {
return {
post: {
id: '',
title: '',
body: ''
}
};
},
created() {
this.getPosts();
},
methods: {
addPost(){
fetch('api/post', {
method: 'post',
body: JSON.stringify(this.post),
headers: {
'content-type': 'apllication/json',
'X-CSRF-TOKEN': document.querySelector("meta[property='csrf-token']").getAttribute("content");
}
})
.then(response => response.json())
.then(data => {
this.getPosts();
})
.catch(err => console.log(err));
}
}
};
</script>
Laravel has a middleware called VerifyCsrfToken which is enabled by default. It makes sure all POST requests have a csrf token. This tokens make sure the request is sent from our app only and not from any 3rd party scraper or form submiting tool.
When controller does not get _token in request, it throws error.
Add this 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
on header section in like belwo
You can try this
<script>
export default {
data() {
return {
post: {
id: '',
title: '',
body: ''
}
};
},
created() {
this.getPosts();
},
methods: {
addPost(){
fetch('api/post', {
method: 'post',
body: JSON.stringify(this.post),
headers: {
'content-type': 'apllication/json',
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
})
.then(response => response.text())
.then(data => {
this.getPosts();
})
.catch(err => console.log(err));
}
}
};
</script>

Unable to upload a file using Vue.js to Lumen backend?

I have tried to upload a file using vue.js as front end technology and laravel in the back end. I have tried to pass the file object using formData javascript object but the server responds as the value is not passed.
I have tried to log the file using console.log and it appropriately displays the data.
Consider that I have discarded some field names.
Template Code
<template>
<b-container>
<div align="center">
<b-card class="mt-4 mb-4 col-md-8" align="left" style="padding: 0 0;">
<card-header slot="header" />
<b-form>
<div class="row">
<div class="col-6 col-md-6">
<b-button
type="submit"
variant="success"
class="float-right col-md-5"
v-if="!update"
#click="save"
squared
>
<i class="fas fa-save"></i>
Save
</b-button>
</div>
</div>
<hr style="margin-top: 10px;" />
<b-form-group
label-cols="12"
label-cols-lg="3"
label-for="input-2"
label="Remark: "
label-align-sm="right"
label-align="left"
>
<b-form-textarea
id="textarea"
v-model="record.remark"
rows="2"
max-rows="3"
></b-form-textarea>
</b-form-group>
<b-form-group
label-cols="12"
label-cols-lg="3"
label-for="input-2"
label="Remark: "
label-align-sm="right"
label-align="left"
>
<b-form-file
v-model="record.attachement"
:state="Boolean(record.attachement)"
placeholder="Choose a file..."
drop-placeholder="Drop file here..."
></b-form-file>
</b-form-group>
</b-form>
<status-message ref="alert" />
</b-card>
</div>
</b-container>
</template>
Script Code
<script>
import { mapGetters, mapActions } from "vuex";
export default {
props: ["id", "user_id"],
data: () => ({
record: {
remark: "",
attachement: null
}
}),
methods: {
...mapActions([
"addBenefitRequest",
]),
save(evt) {
evt.preventDefault();
this.$validator.validate().then(valid => {
if (valid) {
const Attachement = new FormData();
Attachement.append("file", this.record.attachement);
var object = {
remark: this.remark
};
this.addBenefitRequest(object, Attachement);
}
});
},
},
computed: mapGetters([
"getStatusMessage",
"getBenefitRequest",
])
};
</script>
Store Code
async addBenefitRequest({ commit }, object, Attachement) {
try {
const response = await axios.post(
commonAPI.BENEFIT_BASE_URL + "/benefit-requests",
object,
Attachement,
{
headers: {
"Content-Type": "multipart/form-data"
}
}
);
commit("pushBenefitRequest", response.data);
commit("setStatusMessage", "Record has been added.");
} catch (error) {
return error
},
Controller Code
public function store(Request $request, Request $request2)
{
$this->validate($request, [
'employee_id' => 'required|string',
'requested_date' => 'required|date',
// 'benefit_type_id' => 'required|string|exists:benefit_types,id',
'reason' => 'required|string',
]);
$this->validate($request2, [
'attachement' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048'
]);
// $success = BenefitRequest::exists($request->employee_id);
// if(!$success)
// return response()->json("Employee doesn't exist", 422);
$id = (string) Str::uuid();
if($request2->attachement)
{
$attachement = $request2->file('attachement')->store('Benefits');
$request->merge(['attachement' => $attachement]);
}
// $request->attachement = $request->file('attachement')->store('Benefits');
$request->merge(['id' => $id]);
BenefitRequest::create($request->all());
return response()->json('Saved', 201);
}
Route
$router->post('',
['uses' => 'BenefitRequestController#store',
'group'=>'Benefit requests',
'parameter'=>'employee_id, requested_date, requested_by, benefit_type_id, reason, remark, status',
'response'=>'<statusCode, statusMessage>'
]);
Here is an example. you can try it
index.vue
`<div id="app">
<div v-if="!image">
<h2>Select an image</h2>
<input type="file" #change="onFileChange">
</div>
<div v-else>
<img :src="image" />
<button #click="removeImage">Remove image</button>
</div>
</div>`
new Vue({
el: '#app',
data: {
image: ''
},
methods: {
onFileChange(e) {
var files = e.target.files || e.dataTransfer.files;
if (!files.length)
return;
this.createImage(files[0]);
},
createImage(file) {
var image = new Image();
var reader = new FileReader();
var vm = this;
reader.onload = (e) => {
vm.image = e.target.result;
};
reader.readAsDataURL(file);
},
removeImage: function (e) {
this.image = '';
}
}
})

File (not image) doc/pdf is not uploaded in vue and laravel

I'm trying to upload a pdf /doc file using VUE and laravel but after submitting, The file shows no value.
I have a VUE component with this form and this script:
export default {
data: function() {
return {
product_list: false,
add_form: true,
edit_form: false,
form: {
po_order_docs: '',
},
errors: {},
};
},
methods: {
processFile() {
this.file = _this.$refs.file.files[0];
let formData = new FormData();
formData.append('this.form.po_order_docs', this.file);
},
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.js"></script>
<label class="col-lg-2 col-form-label">Order Docs</label>
<div class="col-lg-4">
<div class="input-group">
<input type="file" class="custom-file-input m-input" id="file" #change="processFile()" ref="file" />
<label class="custom-file-label" for="file">Choose file</label>
</div>
</div>

Resources