axios post file in vuejs return null - vue.js

I've been trying to post a file with axios to my database.
<template>
<div>
<v-file-input
v-model="file"
label="File input"
id="file"
ref="file"
>
</v-file-input>
<button v-on:click="submitFile()">Submit</button>
</div>
</template>
export default {
name: "Test",
data() {
return {
file: null,
}
},
methods: {
submitFile() {
let formData = new FormData();
formData.append('file', this.file);
axios.post('https://test_file_create/', formData).then(function() {
console.log('SUCCESS!!');
}).catch(function() {
console.log('FAILURE!!');
});
},
},
}
but the problem is in my database i got my file column null it seems it can't post the file although it return "SUCCESS!!" in submitFile function.
can someone please tell me how to solve this problem.

You've made a couple of mistakes here so from most severe to least...
The <v-file-input> component binds the selected file to its model but you are using the handleFileUpload() method to set your file property to the component itself (via $refs).
Try this instead, using v-model with your file property initialised to null instead of an empty string (which doesn't make sense for a File object)
<v-file-input
v-model="file"
label="File input"
id="file"
ref="file"
></v-file-input>
export default {
name: "Test",
data: () => ({ file: null }),
// etc
}
When sending a FormData instance from the browser, do not manually add the content-type header. FormData also requires special mime-boundary tokens which should be handled automatically
axios.post("https://test_file_create/", formData).then(res => ...)
You should only have one root element in your template. <div> is a popular choice
<template>
<div>
<v-file-input
v-model="file"
label="File input"
id="file"
ref="file"
></v-file-input>
<button :disabled="!file" type="button" #click="submitFile">Submit</button>
</div>
</template>

Related

Vue3 objects from Array only rendering after making a small change in component

ers,
Experiencing a strange rendering issue. I am grabbing user data from localForage located in my Vuex store in a promise in the following component:
<template>
<div>
<h1>Users available for test {{ $route.params.id }}</h1>
<v-form>
<div v-if="this.import_complete">
<UserList
:users="users"
/>
</div>
</v-form>
</div>
</template>
<script>
import UserList from './UserList.vue';
export default {
name: 'UserManagement',
components: {
UserList,
},
data: () => ({
users: [],
import_complete: false,
}),
mounted() {
Promise.resolve(this.$store.getters.getUsersByTestId(
this.$route.params.testId,
)).then((value) => {
this.users = value;
this.import_complete = true;
});
},
};
</script>
Since it's a promise, I am setting a boolean import_complete to true, and a div in the template is only passing through the data as a prop when this boolean is true
Next, I am consuming the data in another template, in a for loop.
<template>
<div>
<v-container>
<v-banner v-for="user in this.users" :key="user.index">
{{ user.index }} {{ user.name }} {{ user.profile }}
<template v-slot:actions>
<router-link
:to="`/usering/${user.test}/user/${user.index}`">
<v-btn text color="primary">Open usering analysis</v-btn>
</router-link>
<v-btn text color="warning" #click="deleteUser(user.index)">Delete</v-btn>
</template>
</v-banner>
</v-container>
</div>
</template>
<script>
export default {
name: 'UserList',
props: {
users: Object,
},
methods: {
deleteUser(index) {
this.$store.dispatch('delete_user', index);
},
},
mounted() {
console.log('mounted user list, here come the users');
console.log(this.users);
},
};
</script>
The thing is, the first time it doesn't show anything. Only when I make a tiny change in the last component (can be an Enter followed by a save command) and suddenly the users are displayed on the page.
Interestingly, in the first scenario, the user's array is already filled, I see it in the console (created in the mount method) as well in the Chrome developer Vue tab.
It's probably some kind of Vue thing I am missing? Does someone have a clue?
[edit]
I've changed the code to this, so directly invoking the localForage. It seems to work, but I would still like to understand why the other code won't work.
this.test = this.$store.getters.getTestByTestId(this.$route.params.testId);
this.test.store.iterate((value, key) => {
if (key === (`user${this.$route.params.userId}`)) {
this.user = value;
}
}).then(() => {
this.dataReady = true;
}).catch((err) => {
// This code runs if there were any errors
console.log(err);
});

How to register an array of objects with v-model? Vue 2

The first time that I dive into making this type of form with Vue, the issue is that I can't think of how to save the data inside the foreach that I generate with axios.
Where I would like to save the ID and the option selected with the input select as an object in order to make faster the match in the backend logic.
<template>
<div class="row" v-else>
<div class=" col-sm-6 col-md-6 col-lg-6" v-for="(project, index) in projects" :key="index">
<fieldset class="border p-2 col-11">
<legend class="w-auto col-12">Proyecto: {{project.name}}</legend>
<b-form-group
id="user_id"
label="Reemplazante"
>
<b-form-select
v-model="formProject[index].us"
:options="project.users"
value-field="replacement_user_id"
text-field="replacement_user_name"
#change="addReemplacemet($event,project.id)"
>
<template v-slot:first>
<b-form-select-option value="All">Seleccione</b-form-select-option>
</template>
</b-form-select>
<input type="hidden" name="project" v-model="formProject[index].proj">
</b-form-group>
</fieldset>
</div>
</div>
</template>
<script>
import skeleton from './skeleton/ProjectUserSkeleton.vue'
export default {
name: 'ProjectsUser',
components: { skeleton },
props:{
user: { type: String },
},
data() {
return {
user_id: null,
showProject: false,
projects: [],
loadingProjects: true,
formProject: [
{
us: 'All',
proj: null
}
]
}
},
watch: {
user: function() {
this.viewProjects(this.user)
}
},
methods: {
async getProjects(salesman){
this.loadingProjects = true
await axios.get(route('users.getProjects'),{
params: {
filter_user: salesman
}
})
.then((res)=>{
this.projects = res.data.data
setTimeout(() => {
this.loadingProjects = false
}, 800);
})
},
This is the form:
This is the message error:
At first glance, this looks like an issue with data. Your error suggests that you're trying to read an undefined variable.
It's often undesirable to use an index from iterating one array on another. In your Vue code, the index is projects, but you use it to access the variable formProject. This is usually undesirable because you can no longer expect that index to reference a defined variable (unless you're referencing the array you are currently iterating).
The easiest solution, for now, is to make sure the arrays are of the same length. Then utilizing v-if or other methods to not render the snippet if the variable is not defined.
A more complicated but better solution is restructuring your data such that formProject exists in projects

Unable to Define Variable in Vue

I'm just starting to use VueJS & Tailwind, having never really used anything related to npm before.
I have the below code, making use of Tailwind & Headless UI which through debugging, I know I'm like 99% of the way there... except for the continuous error message
Uncaught ReferenceError: posts is not defined
I know this should be straight forward, but everything I've found either here or with Google hasn't worked. Where am I going wrong?
<template>
<Listbox as="div" v-model="selected">
<ListboxLabel class="">
Country
</ListboxLabel>
<div class="mt-1 relative">
<ListboxButton class="">
<span class="">
<img :src="selected.flag" alt="" class="" />
<span class="">{{ selected.name }}</span>
</span>
<span class="">
<SelectorIcon class="" aria-hidden="true" />
</span>
</ListboxButton>
<transition leave-active-class="" leave-from-class="opacity-100" leave-to-class="opacity-0">
<ListboxOptions class="">
<ListboxOption as="template" v-for="country in posts" :key="country" :value="country" v-slot="{ active, selected }">
<li :class="">
<div class="">
<img :src="country.flag" alt="" class="" />
<span :class="[selected ? 'font-semibold' : 'font-normal', 'ml-3 block truncate']">
{{ country.latin }}
</span>
</div>
<span v-if="selected" :class="">
<CheckIcon class="" aria-hidden="true" />
</span>
</li>
</ListboxOption>
</ListboxOptions>
</transition>
</div>
</Listbox>
</template>
<script>
import { ref } from 'vue'
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from '#headlessui/vue'
import { CheckIcon, SelectorIcon } from '#heroicons/vue/solid'
import axios from 'axios'
export default {
data() {
return {
response: null,
posts: undefined,
};
},
components: {
Listbox,
ListboxButton,
ListboxLabel,
ListboxOption,
ListboxOptions,
CheckIcon,
SelectorIcon,
},
mounted: function() {
axios.get('http://localhost')
.then(response => {
this.posts = response.data;
});
},
setup() {
const selected = ref(posts[30])
return {
selected,
}
},
}
</script>
The offending line is const selected = ref(posts[30]) which I know I need to somehow define posts, but I don't get how?
CAUSE OF YOUR ERROR:
You are trying to access an array element before the array is populated. Thus the undefined error.
EXPLANATION
You are using a mix of composition api and options api. Stick to one.
I am writing this answer assuming you will pick the composition api.
Follow the comments in the below snippet;
<script>
// IMPORT ONMOUNTED HOOK
import { ref, onMounted } from 'vue'
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from '#headlessui/vue'
import { CheckIcon, SelectorIcon } from '#heroicons/vue/solid'
import axios from 'axios'
export default {
// YOU DO NOT NEED TO DEFINE THE DATA PROPERTY WHEN USING COMPOSITION API
/*data() {
return {
response: null,
posts: undefined,
};
},*/
components: {
Listbox,
ListboxButton,
ListboxLabel,
ListboxOption,
ListboxOptions,
CheckIcon,
SelectorIcon,
},
// YOU DO NOT NEED THESE LIFE CYCLE HOOKS; COMPOSITION API PROVIDES ITS OWN LIFECYCLE HOOKS
/*mounted: function() {
axios.get('http://localhost')
.then(response => {
this.posts = response.data;
});
},*/
setup() {
// YOU ARE TRYING TO ACCESS AN ELEMENT BEFORE THE ARRAY IS POPULATED; THUS THE ERROR
//const selected = ref(posts[30])
const posts = ref(undefined);
const selected = ref(undefined);
onMounted(()=>{
// CALL THE AXIOS METHOD FROM WITHIN THE LIFECYCLE HOOK AND HANDLE THE PROMISE LIKE A BOSS
axios.get('http://localhost')
.then((res) => {
selected.value = res[30];
});
});
return {
selected,
}
},
}
</script>
According to your comment; you should first check if the “selected != null” before using ‘selected’ inside the template. You can use a shorthand version like this
<img :src=“selected?.flag” />

VueJS - template variables not reactive with data variable

I'm making a chat system with socket.io and VueJS, so customers can talk to an admin. But when a client connects to the server, the variable in the data() updates. But the template is not updating.
Here is my code:
<template>
<div>
<div class="chats" id="chat">
<div class="chat" v-for="chat in chats">
<b>{{ chat.clientName }}</b>
<p>ID: {{ chat.clientID }}</p>
<div class="jens-button">
<img src="/icons/chat-bubble.svg">
</div>
</div>
</div>
</div>
</template>
<script>
let io = require('socket.io-client/dist/socket.io.js');
let socket = io('http://127.0.0.1:3000');
export default {
name: 'Chats',
data() {
return {
chats: [],
}
},
mounted() {
this.getClients();
this.updateClients();
},
methods: {
getClients() {
socket.emit('get clients', true);
},
updateClients() {
socket.on('update clients', (clients) => {
this.chats = clients;
console.log(this.chats);
});
}
},
}
</script>
Then I get this, the box is empty:
But I need to get this, this will only appear when I force reload the page. I don't know what I'm doing wrong...
Oke, I've found out where the problem was, in another component I used plain javascript which brokes the whole reactive stuff.

How can I upload image in a link on the vue component?

My component vue like this :
<template>
<div>
<ul class="list-inline list-photo">
<li v-for="item in items">
<div class="thumbnail" v-if="clicked[item]">
<img src="https://myshop.co.id/img/no-image.jpg" alt="">
<span class="fa fa-check-circle"></span>
</div>
<a v-else href="javascript:;" class="thumbnail thumbnail-upload"
title="Add Image" #click="addPhoto(item)">
<span class="fa fa-plus fa-2x"></span>
</a>
</li>
</ul>
</div>
</template>
<script>
export default {
props: ['state', 'product'],
data() {
return {
items: [1, 2, 3, 4, 5],
clicked: [] // using an array because your items are numeric
}
}
},
methods: {
addPhoto(item) {
this.$set(this.clicked, item, true)
}
}
}
</script>
If I click a link then it will call method addPhoto
I want if the a link clicked, it will upload image. So it will select the image then upload it and update img with image uploaded.
It looks like the code to upload image will be put in add photo method
I'm still confused to upload image in vue component
How can I solve it?
You can use a component for file picker like this:
<template>
<input v-show="showNative" type="file" :name="name" #change="onFileChanged" :multiple="multiple" :accept="accept"/>
</template>
<script>
export default {
props: {
name: { type: String, required: true },
show: { type: Boolean, Default: false },
multiple: { type: Boolean, default: false },
accept: { type: String, default: "" },
showNative: { type: Boolean, default: false }
},
watch: {
show(value) {
if (value) {
// Resets the file to let <onChange> event to work.
this.$el.value = "";
// Opens select file system dialog.
this.$el.click();
// Resets the show property (sync technique), in order to let the user to reopen the dialog.
this.$emit('update:show', false);
}
}
},
methods: {
onFileChanged(event) {
var files = event.target.files || event.dataTransfer.files;
if (!files.length) {
return;
}
var formData = new FormData();
// Maps the provided name to files.
formData.append(this.name, this.multiple ? files : files[0]);
// Returns formData (which can be sent to the backend) and optional, the selected files (parent component may need some information about files).
this.$emit("files", formData, files);
}
}
}
</script>
And here some information how to use it:
import the component -> declare the directive.
provide a -> is used for the formData creation (is the name which is going to backend).
to display it us the property
Note: sync recommended if needed to be opened multiple times in the same page. Check the bottom examples. ( /!\ Vue 2.3 required for sync /!\ )
listen to #files event to get an array of selected files as parameter
if you want to use it as multiple file select, then provide the property as true.
use prop to filter the files (valid accept types: HTML Input="file" Accept Attribute File Type (CSV)).
when is set to true, the component displays 'select file' button (input type file), otherwise it is hidden, and windows displayed by Js.
ex:
Single select
<file-upload name="fooImport" #files="selectedFile" :show.sync="true" />
ex:
Multiple select
<file-upload name="barUpload" #files="selectedFiles" :show.sync="displayUpload" accept="text/plain, .pdf" />