I have a problem capturing multiple images to my function in vue and pass to the controller. I am trying to modify my blade file which works well when using just the normal form. I am trying to modify to vue but I have a problem with image section. Please assist me on how to achieve this.
my form:
<label for="">Description</label>
<textarea name="description" class="form-control" v-model="description"> </textarea>
<label for="">Images</label>
<input type="file" #change="fieldChange" class="form-control input-sm" name="images[]"
multiple>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
The vue section
data(){
return {
id:'',
price:'',
title:'',
description:'',
location:'',
images:[],
}
},
methods:{
fieldChange(e){
let selectedFiles=e.target.files;
if(!selectedFiles.length){
return false;
}
for(let i=0;i<selectedFiles.length;i++){
this.images.push(selectedFiles[i]);// am able to console the images
}
},
saveImageData(){
var self=this;
axios.post('/senddata',{
title:this.title,
description:this.description,
location:this.location,
images:this.images.file.name,
price:this.price,
})
},
My Laravel function
public function store(Request $request)
{
$product=Products::create([
'title'=>$request['title'],
'description'=>$request['description'],
'price'=>$request['price'],
'location'=>$request['location']
]);
$images= $request->file('images');
foreach ($images as $image){
$move=$image->move(public_path().'/images2/',$image->getClientOriginalName());
if($move){
$imagedata=Images::create([
'title'=>$image->getClientOriginalName(),
'filename'=>$image->getClientOriginalName()
]);
$product->images()->attach([$imagedata->id]);
}
Here is the working code for vuejs part - https://picupload.netlify.app/
https://github.com/manojkmishra/dw_take5/blob/master/src/components/ImageUploader.vue
Vue part
<template>
<div class="uploader"
#dragenter="OnDragEnter"
#dragleave="OnDragLeave"
#dragover.prevent
#drop="onDrop"
:class="{ dragging: isDragging }">
<div class="upload-control" v-show="images.length">
<label for="file">Select files</label>
<button #click="upload">Upload</button>
</div>
<div v-show="!images.length">
<i class="fa fa-cloud-upload"></i>
<p>Drag your images here</p><div>OR</div>
<div class="file-input">
<label for="file">Select files</label>
<input type="file" id="file" #change="onInputChange" multiple>
</div>
</div>
<div class="images-preview" v-show="images.length">
<div class="img-wrapper" v-for="(image, index) in images" :key="index">
<img :src="image" :alt="`Image Uplaoder ${index}`">
<div class="details">
<span class="name" v-text="files[index].name"></span>
<span class="size" v-text="getFileSize(files[index].size)"> </span>
</div>
</div>
</div>
<div v-show="images.length" class="progress">
<div
class="progress-bar progress-bar-info progress-bar-striped"
role="progressbar" :aria-valuenow="progress"
aria-valuemin="0" aria-valuemax="100"
:style="{ width: progress + '%' }"
>
{{ progress}}%
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default
{ data: () => ({ isDragging: false, dragCount: 0, files: [],images: [] ,progress:0}),
methods: {
OnDragEnter(e) { e.preventDefault();
this.dragCount++;
this.isDragging = true;
return false;
},
OnDragLeave(e) { e.preventDefault();
this.dragCount--;
if (this.dragCount <= 0) this.isDragging = false;
},
onInputChange(e) { const files = e.target.files;
Array.from(files).forEach(file => this.addImage(file));
},
onDrop(e) {console.log('ondrop-evnt e=',e)
e.preventDefault();
e.stopPropagation();
this.isDragging = false;
const files = e.dataTransfer.files;
Array.from(files).forEach(file => this.addImage(file));
},
addImage(file) {console.log('addimage file=',file)
if (!file.type.match('image.*')) { this.$toastr.e(`${file.name} is not an image`);
return;
}
this.files.push(file);
const img = new Image(),
reader = new FileReader();
reader.onload = (e) => this.images.push(e.target.result);
reader.readAsDataURL(file);
console.log('addimage this.images=',this.images)
},
getFileSize(size) { const fSExt = ['Bytes', 'KB', 'MB', 'GB'];
let i = 0;
while(size > 900) { size /= 1024; i++; }
return `${(Math.round(size * 100) / 100)} ${fSExt[i]}`;
},
upload() { //this.progress = '0';
const formData = new FormData();
this.files.forEach(file =>
{ formData.append('images[]', file, file.name); });
console.log('upload triggered FormData=',formData)
// resp=axios.post('http://127.0.0.1:8000/sendemail1',this.formData);
// axios.post('http://127.0.0.1:8000/api/imagesupload', formData,
axios.post('https://uat.oms.dowell.com.au/api/imagesupload', formData,
{onUploadProgress:uploadEvent=>{ this.progress=Math.round(uploadEvent.loaded/uploadEvent.total*100);
console.log('upld prges:'+ Math.round(uploadEvent.loaded/uploadEvent.total*100)+'%')
}
})
//axios.post('https://uat.oms.dowell.com.au/api/imagesupload', formData)
.then(response => {
this.$toastr.s('All images uplaoded successfully');
this.images = [];
this.files = [];
this.progress = 0;
})
.catch(() => {
this.$toastr.e('Could not upload the files!');
this.images = [];
this.files = [];
this.progress = 0;
});
}
}
}
</script>
Another version with vuetify
https://github.com/manojkmishra/take5-front-admin/blob/master/src/components/img/imgup.vue
Here is laravel part
public function imagesupload(Request $request){
if (count($request->images)) {
foreach ($request->images as $image) {
$image->store('images');
}
}
return response()->json(["message" => "Done"]);
}
Another way in laravel is here
https://github.com/manojkmishra/take5-api/blob/master/app/Http/Controllers/PicController.php
Related
when I reload browser I can see the post's pics
I have a component referring to the posts, but when you make a post that contains images, they are not updated and the post remains with the images from the previous post, the only way to prevent this from happening is by updating the browser and it should not be like that
Could someone help me to know what is happening, if I should update the component when loading the images or what should I do?
Thanks!
fomPost.vue =>
<template>
<div class="card bg-dark border-left-primary border-right-primary shd mb-4">
<div class="card-body">
<div v-if="status_msg" :class="{ 'alert-green': status, 'alert-danger': !status }" class="alert"
role="alert">{{ status_msg }}
</div>
<div class="form-group">
<textarea id="content-body" class="form-control" v-model="body"></textarea>
<a href="javascript:void(0)" class="text-lg float-right" data-toggle="tooltip"
data-title="Agregar imágenes" data-placement="bottom" #click="toggle()">
<i class="fas fa-cloud-upload-alt"></i>
</a>
</div>
<div v-show="isOpen" :style="'margin-top: 2rem'">
<div class="uploader"
#dragenter="OnDragEnter"
#dragleave="OnDragLeave"
#dragover.prevent
#drop="onDrop"
:class="{ dragging: isDragging }">
<div class="upload-control" v-show="images.length">
<label for="file">Agregar más imágenes</label>
</div>
<div v-show="!images.length">
<p>Arrastra las imágenes ó</p>
<div class="file-input">
<label for="file">Selecciónalas</label>
<input type="file" id="file" #change="onInputChange"
accept="image/x-png,image/gif,image/jpeg" multiple>
</div>
</div>
</div>
<div class="images-preview" v-show="images.length">
<div class="img-wrapper" v-for="(image, index) in images" :key="index">
<div class="thumbnail" :style="`background-image: url(${image.replace(/(\r\n|\n|\r)/gm)})`">
<div class="options">
<div>
<a href="javascript:void(0)" class="text-light" uk-tooltip="title: Eliminar"
#click="removeimage(index)">
<i class="fas fa-trash-alt"></i>
</a>
</div>
<div>
<a href="javascript:void(0)" class="text-light" uk-tooltip="title: Previsualizar"
uk-toggle="target: #modal-media-image" #click="showImage(image)">
<i class="fas fa-search-plus"></i>
</a>
</div>
</div>
</div>
<div class="details">
<span class="size" v-text="getFileSize(files[index].size)"></span>
</div>
<div id="modal-media-image" class="uk-flex-top" uk-modal>
<div class="uk-modal-dialog uk-width-auto uk-margin-auto-vertical">
<button class="uk-modal-close-outside" type="button" uk-close></button>
<img width="1024px" :src="dialogImageUrl">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-footer bg-opacity-7-dark">
<button type="button" #click="createPost" class="btn btn-primary float-right">
<template v-if="isCreatingPost" class="align-items-center">
<div class="d-flex align-items-center">
<span class="mr-1">Publicando</span>
<div class="loadingio-spinner-dual-ring-botj7pu8xqc">
<div class="ldio-ifliw7yncz">
<div></div>
<div>
<div></div>
</div>
</div>
</div>
</div>
</template>
<template v-else>
Publicar
</template>
</button>
</div>
</div>
</template>
<script>
import {mapActions} from 'vuex'
export default {
name: "FormPostText",
props: ['profile'],
data() {
return {
dialogImageUrl: "",
dialogVisible: false,
isDragging: false,
dragCount: 0,
files: [],
images: [],
status_msg: "",
status: "",
isCreatingPost: false,
title: "",
body: "",
isOpen: false
}
},
mounted() {
$("#content-body").emojioneArea({
placeholder: "¿Qué estás pensando?",
searchPlaceholder: "Buscar",
buttonTitle: "Usa la tecla [TAB] para insertarlos más rápido",
pickerPosition: 'bottom',
filtersPosition: "bottom",
searchPosition: "top"
});
},
methods: {
...mapActions({
create: "post/makePost"
}),
showImage(file) {
this.dialogImageUrl = file;
this.dialogVisible = true;
},
OnDragEnter(e) {
e.preventDefault();
this.dragCount++;
this.isDragging = true;
return false;
},
OnDragLeave(e) {
e.preventDefault();
this.dragCount--;
if (this.dragCount <= 0)
this.isDragging = false;
},
onInputChange(e) {
const files = e.target.files;
Array.from(files).forEach(file => this.addImage(file));
},
onDrop(e) {
e.preventDefault();
e.stopPropagation();
this.isDragging = false;
const files = e.dataTransfer.files;
Array.from(files).forEach(file => this.addImage(file));
},
addImage(file) {
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
this.showNotification("La imágen no puede ser mayor a 2MB.");
return;
}
if (!file.type.match('image.*')) {
this.showNotification(`${file.name} no es una imágen`);
return;
}
this.files.push(file);
const img = new Image(), reader = new FileReader();
reader.onload = (e) => this.images.push(e.target.result);
reader.readAsDataURL(file);
return isLt2M;
},
removeimage: function (index) {
this.images.splice(index, 1);
},
getFileSize(size) {
const fSExt = ['Bytes', 'KB', 'MB', 'GB'];
let i = 0;
while (size > 900) {
size /= 1024;
i++;
}
return `${(Math.round(size * 100) / 100)} ${fSExt[i]}`;
},
toggle() {
this.isOpen = !this.isOpen;
},
createPost() {
var body = $("#content-body").val();
var text = emojione.toImage(body);
if (!this.validateForm()) {
return false;
}
this.isCreatingPost = true;
const formData = new FormData();
formData.append("user", this.profile.uid);
formData.append("body", text);
this.files.forEach(file => {
formData.append('images[]', file, file.name);
});
this.create(formData).then((res) => {
if (res.data.status === 0) {
this.status = code;
this.showNotification(res.data.msg);
}
document.querySelector(".emojionearea-editor").innerHTML = '';
this.isCreatingPost = false;
this.images = [];
this.files = [];
this.isOpen = false;
let post = res.data;
this.$emit("new", post);
}).catch(error => {
console.log(error)
this.isCreatingPost = false;
});
},
validateForm() {
if (!$("#content-body").val()) {
this.status = false;
this.showNotification("Debes escribir algo para publicar...");
return false;
}
return true;
},
showNotification(message) {
this.$swal.fire({
icon: 'error',
html: message
});
}
}
}
</script>
Post.vue =>
<template>
<div id="timeline">
<div v-if="authenticated.username === username || isFriend">
<FormPost :profile="profile" #new="addPostText"></FormPost>
</div>
<!--<pre>{{postsArr}}</pre>-->
<div v-if="postsCount > 0">
<div v-for="(post, index) in postsArr" :key="index">
<div class="cardbox shadow-lg bg-opacity-5-dark shd">
<div class="cardbox-heading">
<div class="dropdown float-right">
<button class="btn btn-sm btn-dark btn-circle" data-toggle="dropdown"
data-boundary="window">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu dropdown-scale dropdown-menu-right" role="dropdown">
<template v-if="post.user.id === user.uid || post.friend.id === user.uid">
<b-dropdown-item href="javascript:void(0)" #click="deletePost(post.post.id, index)">
<span class="uk-margin-small-right" uk-icon="icon: trash"></span> Eliminar
</b-dropdown-item>
<b-dropdown-divider></b-dropdown-divider>
</template>
<b-dropdown-item href="javascript:void(0)">
<span class="uk-margin-small-right text-danger" uk-icon="icon: warning"></span>
Reportar
</b-dropdown-item>
</div>
</div>
<div class="media m-0">
<div class="d-flex mr-3">
<a v-bind:href="post.user.username">
<img class="img-fluid rounded-circle" v-bind:src="post.friend.photo" alt="User">
</a>
</div>
<div class="media-body">
<p class="m-0"><a v-bind:href="post.friend.username">{{ '#' + post.friend.username }}</a></p>
<small><span><i
class="far fa-clock"></i> {{ since(post.post.created_at) }}</span></small>
</div>
</div>
</div>
<div class="cardbox-item">
<p class="mx-4">{{ post.post.body | setEmoji }}</p>
<div v-if="post.images.length > 0">
<!--<photo-grid
:box-height="'600px'"
:box-width="'100%'"
:boxBorder="0"
:excess-text="'+ {{count}} imágenes'"
uk-lightbox="animation: slide"
>
<img v-for="(imahe, index) in post.images" v-bind:src="`http://127.0.0.1:8000/storage/images/${post.friend.id}/${imahe.url}`" :key="index"/>
</photo-grid>-->
<ImagesGrid :images="post.images" :idFriend="post.friend.id"
uk-lightbox="animation: slide"></ImagesGrid>
</div>
</div>
<div class="cardbox-base">
<ul class="float-right">
<li><a><i class="fa fa-comments"></i></a></li>
<li><a><em class="mr-5">{{ post.comments_count || comments_count }}</em></a></li>
<li><a><i class="fa fa-share-alt"></i></a></li>
<li><a><em class="mr-3">03</em></a></li>
</ul>
<ul>
<li>
<Likes :postid="post.post.id" :user="user"></Likes>
</li>
<li><a href="#"><img
src="http://www.themashabrand.com/templates/bootsnipp/post/assets/img/users/3.jpeg"
class="img-fluid rounded-circle" alt="User"></a></li>
<li><a href="#"><img
src="http://www.themashabrand.com/templates/bootsnipp/post/assets/img/users/1.jpg"
class="img-fluid rounded-circle" alt="User"></a></li>
<li><a href="#"><img
src="http://www.themashabrand.com/templates/bootsnipp/post/assets/img/users/5.jpg"
class="img-fluid rounded-circle" alt="User"></a></li>
<li><a href="#"><img
src="http://www.themashabrand.com/templates/bootsnipp/post/assets/img/users/2.jpg"
class="img-fluid rounded-circle" alt="User"></a></li>
<li><a><span>242 Likes</span></a></li>
</ul>
</div>
<CommentsPost
#new="commentsCount"
:postid="post.post.id"
:postuserid="user.uid"
:user="user"
></CommentsPost>
</div>
</div>
<nav class="pagination-outer">
<ul class="pagination">
<li class="page-item" :class="[pagination.current_page > 1 ? '' : 'disabled']">
<a href="#" class="page-link" aria-label="Previous" #click.prevent="changePage(pagination.current_page - 1)">
<span aria-hidden="true">«</span>
</a>
</li>
<li class="page-item" v-for="page in pagesNumber" :class="[page == isActived ? 'active' : '']">
<a class="page-link" href="#" v-bind:data-hover="page" #click.prevent="changePage(page)">{{page}}</a>
</li>
<li class="page-item" :class="[pagination.current_page < pagination.last_page ? '' : 'disabled']">
<a href="#" class="page-link" aria-label="Next" #click.prevent="changePage(pagination.current_page + 1)">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</div>
<div v-else class="card bg-opacity-5-dark mb-4">
<div class="card-body">
<span class="text-light">No tiene posts aún.</span>
</div>
</div>
</div>
</template>
<script>
import Vue from 'vue'
var moment = require("moment");
moment.locale("es");
import {mapActions, mapGetters} from 'vuex'
import FormPost from "../Posts/FormPost";
import Likes from "../Posts/Likes";
import CommentsPost from "../Posts/CommentsPost";
import ImagesGrid from "../Posts/ImagesGrid";
export default {
name: "Posts",
props: {
username: {
type: String,
required: true
},
profile: {
type: Object,
required: true
},
user: {
type: Object,
required: true
},
isFriend: {
type: Boolean,
required: true
}
},
data() {
return {
page: 0,
offset: 4,
comments_count: ''
}
},
mounted() {
this.getPosts();
},
beforeRouteEnter(to, from, next) {
this.getPosts()
next()
},
beforeRouteUpdate(to, from, next) {
this.getPosts()
next()
},
computed: {
...mapGetters({
authenticated: "auth/authenticated",
postsArr: "post/postsLists",
pagination: "post/postsPagination",
postsCount: "post/postsCount"
}),
isActived: function() {
return this.pagination.current_page;
},
pagesNumber: function () {
if(!this.pagination.to) {
return [];
}
var from = this.pagination.current_page - this.offset;
if(from < 1) {
from = 1;
}
var to = from + (this.offset * 2);
if(to >= this.pagination.last_page) {
to = this.pagination.last_page;
}
var pagesArray = [];
while(from <= to) {
pagesArray.push(from);
from++;
}
return pagesArray;
}
},
methods: {
...mapActions({
getPostsByUser: "post/fetchPosts",
removePost: "post/deletePost",
}),
async getPosts(page) {
await this.getPostsByUser(page);
await this.$forceUpdate();
},
async addPostText(post) {
await this.postsArr.unshift(post);
await this.$forceUpdate();
await this.changePage(1);
},
since(d) {
return moment(d).fromNow();
},
commentsCount(count) {
this.comments_count = count;
},
deletePost(post, index) {
this.postsArr.splice(index, 1)
this.removePost(post)
},
changePage(page) {
this.pagination.current_page = page;
this.getPosts(page);
}
},
filters: {
setEmoji: (value) => {
var rx = /<img\s+(?=(?:[^>]*?\s)?class="[^">]*emojione)(?:[^>]*?\s)?alt="([^"]*)"[^>]*>(?:[^<]*<\/img>)?/gi;
return value.replace(rx, "$1")
}
},
components: {
FormPost,
Likes,
CommentsPost,
ImagesGrid
},
watch: {
'$route': 'getPosts'
}
ImagesGrid.vue =>
<template>
<photo-grid
:box-height="'600px'"
:box-width="'100%'"
:boxBorder="0"
:excess-text="'+ {{count}} imágenes'"
uk-lightbox="animation: slide"
>
<a :href="`http://127.0.0.1:8000/storage/images/${idFriend}/${imahe.url}`"
v-for="(imahe, index) in images"
v-bind:data-image="`http://127.0.0.1:8000/storage/images/${idFriend}/${imahe.url}`"
:key="index">
</a>
</photo-grid>
</template>
<script>
export default {
props: ['idFriend', 'images'],
watch: {
$route(to, from, next) {
this.$forceUpdate();
next()
}
}
};
</script>
Post.js =>
import axios from 'axios'
import store from './index'
export default {
namespaced: true,
state: {
posts: [],
posts_count : 0,
pagination: {
'total': 0,
'current_page': 0,
'per_page': 0,
'last_page': 0,
'from': 0,
'to': 0,
}
},
getters: {
postsLists(state) {
return state.posts;
},
postsCount(state) {
return state.posts_count
},
postsPagination(state) {
return state.pagination
}
},
mutations: {
SET_POST_COLLECTION (state, data) {
state.posts = data.posts.data ;
state.pagination = data.paginate;
state.posts_count = data.paginate.total;
}
},
actions: {
makePost({commit,dispatch}, data) {
return new Promise((resolve, reject) => {
axios.post('user/post/create', data)
.then((response) => {
dispatch("fetchPosts", 1)
resolve(response);
})
.catch((error) => {
reject(error);
});
})
},
fetchPosts({commit}, page) {
return new Promise((resolve, reject) => {
axios.get(`user/${store.state.user.profile.username}/posts?page=${page}`)
.then((response) => {
//console.log(response.data.posts.data);
commit('SET_POST_COLLECTION', response.data);
resolve(response);
})
.catch((error) => {
reject(error);
});
})
},
deletePost({commit}, id) {
return new Promise((resolve, reject) => {
let params = {'id': id};
axios.post('user/post/delete', params)
.then((response) => {
console.log(response.data)
resolve(response);
})
.catch((error) => {
reject(error);
});
})
},
createComment({commit}, comment) {
return new Promise((resolve, reject) => {
axios.post('user/post/comment/create', comment)
.then((response) => {
console.log(response.data)
resolve(response);
})
.catch((error) => {
reject(error);
});
})
}
}
}
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 = '';
}
}
})
I m developing an e-commerce application with react hooks in search component result is shown beneath the component I want to render on search Result component how to redirect search result on the search Result component
import React, { useState, useEffect } from "react";
import { Link, Redirect } from "react-router-dom";
import { getCategories, list } from "./apiCore";
import SearchResult from "./SearchResult";
const Search = () => {
const [data, setData] = useState({
categories: [],
category: "",
search: "",
results: [],
searched: false,
});
const { categories, category, search, results, searched } = data;
const loadCategories = () => {
getCategories().then(data => {
if (data.error) {
console.log(data.error);
} else {
setData({ ...data, categories: data });
}
});
};
useEffect(() => {
loadCategories();
}, []);
const searchData = () => {
// console.log(search, category);
if (search) {
list({ search: search || undefined, category: category }).then(
response => {
if (response.error) {
console.log(response.error);
} else {
setData({ ...data, results: response, searched: true });
}
}
);
}
};
const searchSubmit = e => {
e.preventDefault();
searchData();
};
const handleChange = name => event => {
setData({ ...data, [name]: event.target.value, searched: false });
};
const searchMessage = (searched, results) => {
if (searched && results.length > 0) {
return `Found ${results.length} products`;
}
if (searched && results.length < 1) {
return `No products found`;
}
};
const searchedProducts = (results = []) => {
return (
<div>
<h2 className="text-muted mb-4">
{searchMessage(searched, results)}
</h2>
<div className="row">
{results.map((product, i) => (
<CarouselCard key={i} product={product} />
))}
</div>
</div>
);
};
return (
<div className="site-navbar-top">
<div className="container">
<div className="row align-items-center">
<div className="col-6 col-md-4 order-2 order-md-1 site-search-icon text-left">
<form className="site-block-top-search" onSubmit={searchSubmit}>
<span className="icon icon-search2"></span>
<input type="text" className="form-control border-0" placeholder="Search" onChange={handleChange("search")} />
</form>
</div>
<div className="col-12 mb-3 mb-md-0 col-md-4 order-1 order-md-2 text-center">
<div className="site-logo">
<Link to="/" className="js-logo-clone">Shoppers</Link>
</div>
</div>
<div className="col-6 col-md-4 order-3 order-md-3 text-right">
<div className="site-top-icons">
<ul>
<li><Link to="#"><span className="icon icon-person" /></Link></li>
<li><Link to="#"><span className="icon icon-heart-o" /></Link></li>
<li>
<Link to="cart.html" className="site-cart">
<span className="icon icon-shopping_cart" />
<span className="count">2</span>
</Link>
</li>
</ul>
</div>
</div>
</div>
</div>
{searchedProducts(results)}
</div>
)
}
export default Search;
The main part of the hook is pretty straightforward, but I'm having a difficult time finding a nice way to handle that redirect. The current working solution is to wrap the functional component with withRouter, then pass props.history
I am making a simple website that has a feature to upload images. I tried it Laravel way which I made it in blade template and it works fine. Now I am trying to make it inside Vue Components
Here's my Create.vue
<template>
<div>
<div class="row">
<input type="hidden" name="_token" :value="csrf">
<div class="col-md-5">
<div class="detail-container">
<label for="title">Book Title:</label>
<input type="text" name="title" id="title" v-model="book_title" class="form-control">
</div>
<div class="detail-container">
<label for="title">Book Description:</label>
<textarea type="text" name="description" id="description" v-model="book_description" class="form-control" rows="5"></textarea>
</div>
<div class="detail-container">
<label for="title">Tags:</label>
<multiselect v-model="tags" :show-labels="false" name="selected_tags" :hide-selected="true" tag-placeholder="Add this as new tag" placeholder="Search or add a tag" label="name" track-by="id" :options="tagsObject" :multiple="true" :taggable="true" #tag="addTag" #input="selectTags">
<template slot="selection" slot-scope="tags"></template>
</multiselect>
</div>
</div>
<div class="col-md-7">
<!-- BOOK COVER WILL GO HERE -->
<div class="detail-container">
<label>Book Cover:</label>
<input type="file" class="form-control-file" id="book_cover" name="selected_cover" #change="onFileChange">
<small id="fileHelp" class="form-text text-muted">After you select your desired cover, it will show the preview of the photo below.</small>
<div id="preview">
<img v-if="url" :src="url" height="281" width="180" />
</div>
</div>
</div>
<div class="detail-container" style="margin-top: 20px;">
<button class="btn btn-primary" #click="saveBook()">Next</button>
</div>
</div>
</div>
</template>
<script>
import Multiselect from 'vue-multiselect'
// register globally
Vue.component('multiselect', Multiselect)
export default {
// OR register locally
components: { Multiselect },
data () {
return {
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
url: null,
selected_cover: null,
tags: [],
tagsObject: [],
selected_tags: [],
book_title: '',
book_description: ''
}
},
methods: {
getTags() {
let vm = this;
axios.get('/admin/getTags').then(function(result){
let data = result.data;
for(let i in data) {
vm.tagsObject.push({id: data[i].id, name: data[i].name});
}
});
},
addTag (newTag) {
const tag = {
name: newTag,
id: newTag.substring(0, 2) + Math.floor((Math.random() * 10000000))
}
this.tagsObject.push(tag);
this.tags.push(tag);
},
selectTags(value) {
this.selected_tags = value.map(a=>a.id);
},
onFileChange(e) {
const file = e.target.files[0];
this.url = URL.createObjectURL(file);
this.selected_cover = file;
},
saveBook() {
const fd = new FormData();
fd.append('image', this.selected_cover, this.selected_cover.name)
console.log(this.selected_cover);
var book_details = {
'title': this.book_title,
'description': this.book_description,
'book_cover': this.selected_cover,
'tags': this.selected_tags
};
axios.post('/admin/saveBook', book_details).then(function(result){
console.log('done')
})
}
},
created() {
this.getTags();
}
}
</script>
<!-- New step!
Add Multiselect CSS. Can be added as a static asset or inside a component. -->
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
and here's my controller
public function store(Request $request)
{
$this->validate(request(), [
'title' => 'required|min:5',
'description' => 'required|min:10',
'book_cover' => 'required|image|mimes:jpeg,jpg,png|max:10000'
]);
// File Upload
if($request->hasFile('book_cover')) {
$fileNameWithExt = $request->file('book_cover')->getClientOriginalName();
// GET FILE NAME
$filename = pathinfo($fileNameWithExt, PATHINFO_FILENAME);
// GET EXTENSION
$extension = $request->file('book_cover')->getClientOriginalExtension();
// File Unique Name
$fileNameToStore = $filename. '_'. time().'.'.$extension;
$path = $request->file('book_cover')->storeAs('public/book_covers', $fileNameToStore);
} else {
$fileNameToStore = 'noimage.jpg';
}
$book = new Book;
$book->title = request('title');
$book->description = request('description');
$book->book_cover = $fileNameToStore;
$book->save();
$book->tags()->sync($request->tags, false);
return back()->with('success', 'Book Created Successfully!');
}
I never touched my controller because this is what I used when I do this feature in Laravel Way but when I save it, the details are being saved but the image is not uploading instead it saves noimage.jpg in the database. Does anyone know what I am doing wrong?
i tried to add this part const fd = new FormData(); in book_details but when i console.log(fd) it returned no data.
you should pass the fd object and not the book_details in your POST request.
you could do it something like this.
onFileChange(e) {
const file = e.target.files[0];
// this.url = URL.createObjectURL(file);
this.selected_cover = file;
},
saveBook() {
const fd = new FormData();
fd.append('image', this.selected_cover)
fd.append('title', this.book_title)
fd.append('description', this.book_description)
fd.append('book_cover', URL.createObjectURL(this.selected_cover))
fd.append('tags', this.selected_tags)
axios.post('/admin/saveBook', fd).then(function(result){
console.log('done')
})
}
and also, you can't just console.log the fd in the console. what you can do instead is something like this
for (var pair of fd.entries()) {
console.log(pair[0]+ ', ' + pair[1]);
}
FormData is a special type of object which is not stringifyable and cannot just be printed out using console.log. (link)
Hello I am building one project. Where user can send up to 5 images and up to 10 songs with the text. But when I send request to the server, where I handle with Laravel, I can't get those files.
// my data object from VueJS
data() {
return {
formData: new FormData(),
pikir: {
body: '',
},
isLoading: false,
images: [],
songs: [],
}
}
// imagePreview method from VuejS
imagePreview(event) {
let input = event.target;
if (input.files[0]) {
if (input.files.length <= 5) {
for (let i = 0; i < input.files.length; i++) {
let reader = new FileReader();
reader.onload = e => {
this.images.push(e.target.result);
}
reader.readAsDataURL(input.files[i]);
}
Array.from(Array(event.target.files.length).keys())
.map(x => {
this.formData
.append('images',
event.target.files[x], event.target.files[x].name);
});
} else {
alert('You can upload up to 5 images');
}
}
}
// musicPreview method from VueJS
musicPreview(event) {
let input = event.target;
if (input.files.length <= 5) {
for (let i = 0; i < input.files.length; i++) {
this.songs.push(input.files[i].name.replace('.mp3', ''));
}
Array.from(Array(event.target.files.length).keys())
.map(x => {
this.formData
.append('songs',
event.target.files[x], event.target.files[x].name);
});
} else {
alert('You can upload up to 10 songs');
}
}
// sharePikir method from VueJS
sharePikir() {
this.formData.append('body', this.pikir.body);
axios.put('/api/pikirler', this.formData)
.then(response => {
this.pikir.body = '';
this.isLoading = false;
})
.catch(() => {
this.isLoading = false;
});
},
<form action="#" id="share-pikir">
<label for="pikir">
Näme paýlaşmak isleýärsiňiz? {{ pikirSymbolLimit }}
</label>
<input type="text" name="pikir" id="pikir" maxlength="255"
v-model="pikir.body"
#keyup="handleSymbolLimit()">
<div class="emoji">
<i class="fa fa-smile-o"></i>
</div>
<div class="photo-music left">
<label for="music-upload">
<i class="fa fa-music"></i>
</label>
<input type="file" name="songs[]" id="music-upload"
accept="audio/mp3"
#change="musicPreview($event)"
multiple>
<i class="divider"></i>
<label for="image-upload">
<i class="fa fa-image"></i>
</label>
<input type="file" name="images[]" id="image-upload"
#change="imagePreview($event)"
multiple>
</div>
<div class="share right">
<button
:class="{ 'btn-medium': true, 'is-loading': isLoading }"
#click.prevent="sharePikir($event)">
Paýlaşmak
</button>
</div>
</form>
I put my front end stuff above and in my Laravel side I just return all requests:
public function store(){
return request()->all();
}
And these are what I get from request:
I couldn't found what is wrong. Thank you in advance.
Oh yeah! you're missing this part. You need to define this.
axios.defaults.headers.post['Content-Type'] = 'multipart/form-data';
this my solutuion
export default {
data (){
return {
attachment : { name : null,file: null }
}
},
methods : {
onFileChange(event) {
this.attachment.file = event.target.files[0]
},
attachmentCreate() {
var form = new FormData();
form.append('name',this.attachment.name);
form.append('file',this.attachment.file);
this.$http.post('attachment',form).then(response=>{
console.log("oke")
})
},
}
}
<input type="file" class="form-control" #change="onFileChange">
Check this post, maybe your problem is solved:
[https://stackoverflow.com/questions/55895941/success-upload-file-via-postman-but-fail-on-front-end-vue/73698108#73698108][1]