Vue with Axios drop down pre populate - vue.js

I'm trying to figure out how to pre-populate my vue drop down.
When I click on the drop down it doesn't show anything, but when I click off of it and back on it, it will show the options. This is not the behavior I want. I would like it to show the options when clicking on it once.
<template>
<select v-model='selectedApp' #click="populateApp(results)">
<option value="" disabled> Select an application</option>
<option v-for="result in results">{{ result.name }} {{result.id}}</option>
</select>
</template>
<script>
import axios from 'axios'
export default{
data(){
return {
selectedApp: '',
results: []
}
},
methods: {
populateApp: function(event){
axios.get('/apdata',{params: {query: this.query}})
.then(response => {
this.results = response.data;
console.log(this.results);
});
}
}
}
</script>
Thanks for you help!

You can do something like this. And you forgot to bind the value to option. Here's sample
<template>
<div>
<select v-model="selectedApp">
<option selected disabled value="">Choose</option>
<option v-for="result in results" :value="result.id">{{ result.name }} {{ result.id }}</option>
</select>
<div>selectedApp: {{ selectedApp }}</div>
</div>
</template>
export default {
data() {
return {
selectedApp: "",
results: []
}
},
async mounted() {
try {
const response = await axios.get('/apdata', { params: { query: this.query } })
this.results = response.data
} catch(err) {
console.log(err)
}
}
}

Here is you answer using #Naren suggested
import axios from 'axios'
export default {
data() {
return {
selectedApp: '',
results: []
}
},
mounted() {
axios.get('/apdata', {
params: {
query: this.query
}
})
.then(response => {
this.results = response.data;
console.log(this.results);
})
},
props:{},
....
}
If you are using nuxt i suggest using AsyncData as it will automatically bind the data to the property to be used
async asyncData({ app, params }) {
const { data } = await app.$axios.get(`path/${params.code}`)
return { results: data.error ? [] : data }
//data(){ results:[] } does not need to be defined as this will be taken care of by asyncData
}
If you are using vuejs3 you will need to do the same thing as mounted hook except in setup() additionally you may need to import the router
import { useRoute } from 'vue-router'
export default {
setup(props, ctx) {
const route = useRoute()
onMounted(() => {
const id = route.params.id
///add code here
})
}
}

Related

how to update component when props changes in nuxt

I want to fetch data everytime when props changes in component and display it without reloading page.
pages/invoice/index.vue:
<template>
<div>
<b-table-column
field="InvoiceNo"
label="Invoice No"
sortable
v-slot="props"
>
<a #click="selectInvoice(props.row.id)">
{{ props.row.invoiceNumber }}
</a>
</b-table-column>
<Invoice :invoiceId="selectedInvoice" />
</div>
</template>
<script>
import axios from "axios";
import Invoice from "../../../components/Invoice.vue";
export default {
components: {
Invoice,
},
data() {
return {
selectedInvoice: "",
}
},
methods: {
selectInvoice(invoiceId) {
this.selectedInvoice = invoiceId;
},
}
}
</script>
components/Invoice.vue:
<script>
import axios from "axios";
export default {
props: ["invoiceId"],
data() {
return {
invoiceData: "",
};
},
watch: {
invoiceId: function (newVal, oldVal) {
this.fetchData(newVal)
},
deep: true,
immediate: true,
},
methods: {
async fetchData(invoiceId) {
let { data: invoiceDetails } = await axios.get(
`${process.env.backendapi}/invoice/byid?invoiceId=${invoiceId}`
);
return {
invoiceData: invoiceDetails,
};
},
},
};
</script>
When I select/change invoice, I can see the backend api getting called everytime with selected invoice, but invoiceData is always blank. The returned result is not getting updated in invoiceData.
I think you want the following in the fetchData method
this.invoiceData = invoiceDetails
Instead of
return {}
Only the already existing data and fetch vue/nuxt functions need to return an object

Vue 3 access child component from slots

I am currently working on a custom validation and would like to, if possible, access a child components and call a method in there.
Form wrapper
<template>
<form #submit.prevent="handleSubmit">
<slot></slot>
</form>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
setup(props, { slots }) {
const validate = (): boolean => {
if (slots.default) {
slots.default().forEach((vNode) => {
if (vNode.props && vNode.props.rules) {
if (vNode.component) {
vNode.component.emit('validate');
}
}
});
}
return false;
};
const handleSubmit = (ev: any): void => {
validate();
};
return {
handleSubmit,
};
},
});
</script>
When I call slot.default() I get proper list of child components and can see their props. However, vNode.component is always null
My code is based from this example but it is for vue 2.
If someone can help me that would be great, or is this even possible to do.
I found another solution, inspired by quasar framework.
Form component provide() bind and unbind function.
bind() push validate function to an array and store in Form component.
Input component inject the bind and unbind function from parent Form component.
run bind() with self validate() function and uid
Form listen submit event from submit button.
run through all those validate() array, if no problem then emit('submit')
Form Component
import {
defineComponent,
onBeforeUnmount,
onMounted,
reactive,
toRefs,
provide
} from "vue";
export default defineComponent({
name: "Form",
emits: ["submit"],
setup(props, { emit }) {
const state = reactive({
validateComponents: []
});
provide("form", {
bind,
unbind
});
onMounted(() => {
state.form.addEventListener("submit", onSubmit);
});
onBeforeUnmount(() => {
state.form.removeEventListener("submit", onSubmit);
});
function bind(component) {
state.validateComponents.push(component);
}
function unbind(uid) {
const index = state.validateComponents.findIndex(c => c.uid === uid);
if (index > -1) {
state.validateComponents.splice(index, 1);
}
}
function validate() {
let valid = true;
for (const component of state.validateComponents) {
const result = component.validate();
if (!result) {
valid = false;
}
}
return valid;
}
function onSubmit() {
const valid = validate();
if (valid) {
emit("submit");
}
}
}
});
Input Component
import { defineComponent } from "vue";
export default defineComponent({
name: "Input",
props: {
rules: {
default: () => [],
type: Array
},
modelValue: {
default: null,
type: String
}
}
setup(props) {
const form = inject("form");
const uid = getCurrentInstance().uid;
onMounted(() => {
form.bind({ validate, uid });
});
onBeforeUnmount(() => {
form.unbind(uid);
});
function validate() {
// validate logic here
let result = true;
props.rules.forEach(rule => {
const value = rule(props.modelValue);
if(!value) result = value;
})
return result;
}
}
});
Usage
<template>
<form #submit="onSubmit">
<!-- rules function -->
<input :rules="[(v) => true]">
<button label="submit form" type="submit">
</form>
</template>
In the link you provided, Linus mentions using $on and $off to do this. These have been removed in Vue 3, but you could use the recommended mitt library.
One way would be to dispatch a submit event to the child components and have them emit a validate event when they receive a submit. But maybe you don't have access to add this to the child components?
JSFiddle Example
<div id="app">
<form-component>
<one></one>
<two></two>
<three></three>
</form-component>
</div>
const emitter = mitt();
const ChildComponent = {
setup(props, { emit }) {
emitter.on('submit', () => {
console.log('Child submit event handler!');
if (props && props.rules) {
emit('validate');
}
});
},
};
function makeChild(name) {
return {
...ChildComponent,
template: `<input value="${name}" />`,
};
}
const formComponent = {
template: `
<form #submit.prevent="handleSubmit">
<slot></slot>
<button type="submit">Submit</button>
</form>
`,
setup() {
const handleSubmit = () => emitter.emit('submit');
return { handleSubmit };
},
};
const app = Vue.createApp({
components: {
formComponent,
one: makeChild('one'),
two: makeChild('two'),
three: makeChild('three'),
}
});
app.mount('#app');

Vue.js return data from axios

<template>
<div class="comment">
<div v-for="(comment, index) in comments" :key="index">
{{ getUser(comment.student_idx) }}
</div>
</div>
</template>
<script>
import axios from 'axios'
import server from '#/models/server'
export default {
data() {
return {
url: server,
comments: {}
}
},
props: ['idx'],
created() {
axios.get(`${this.url}/bamboo/reply?post=${this.idx}`)
.then(response => {
if (response.data.status === 200) {
this.comments = response.data.data.replies
}
})
},
methods: {
getUser (idx) {
axios.get(`${this.url}/member/student/${idx}`)
.then(response => {
if (response.data.status === 200) {
return response.data.data.member.name
}
})
}
}
}
</script>
I would like to load the comments at created and print them out using v-for.
In v-for, I would like to load the member.name from each comment
But {{ getUser(comment.student_idx) }} is undefined.
I don't know how to return data from axios
Help me please!!
Your method should not be async for stable run code. My recomendation is next code:
<template>
<div class="comment">
<div v-for="(comment, index) in comments" :key="index">
{{ comments['user'] }}
</div>
</div>
</template>
<script>
import axios from 'axios'
import server from '#/models/server'
export default {
data() {
return {
url: server,
comments: []
}
},
props: ['idx'],
created() {
axios.get(`${this.url}/bamboo/reply?post=${this.idx}`)
.then(response => {
if (response.data.status === 200) {
this.comments = response.data.data.replies;
if(this.comments)
for(let comment of this.comments){
this.getUser(comment, comment.student_idx);
}
}
})
},
methods: {
getUser (comment, idx) {
axios.get(`${this.url}/member/student/${idx}`)
.then(response => {
if (response.data.status === 200) {
this.$set(comment, 'user', response.data.data.member.name);
}
})
}
}
}
</script>

Nuxt + Vuex mapGetters value is always undefined

I'm using VueX with Nuxt.JS so let's suppose the following code in the file store/search.js:
export const state = () => ({
results: null
});
export const mutations = {
setResults(state, { results }) {
state.results = results;
}
};
export const actions = {
startSearch({ commit, dispatch }, { type, filters }) {
commit("setResults", { type, filters });
}
};
export const getters = {
results: state => state.results
};
Now in my component results.vue, under the computed property I have something like this:
<template>
<button #click="handleSearch">Search</button>
<div v-if="results && results.length" class="results" >
<div v-for="item in results" :key="item.id">
{{item}}
</div>
</div>
</template>
<script>
import { mapActions, mapGetters } from "vuex";
data() {
return {
selected_type: null,
filters: null
};
},
methods: {
setType(type) {
this.selected_type = type;
this.handleSearch();
},
setFilters(filters) {
this.filters = filters;
},
handleSearch() {
this.startSearch({ type: this.selected_type, filters: this.filters });
},
...mapActions("search", {
startSearch: "startSearch"
})
},
computed: {
...mapGetters("search", {
results: "results"
})
}
</script>
My question is: why the item in the for loop (in the template section) always return undefined ?
Thank you very much for your answers.
So far, I found it:
in computed should be an array, not an object so:
...mapGetters("search", [
"results"
]
// Now results is populated.

Adding data to v-text-field from axios response

How can I assign the value retrieved from axios to v-text-field
I have this in my template:
<v-text-field
v-model="name"
label="Name"></v-text-field>
<script>
import axios from 'axios';
export default {
data() {
return {
name: '' // <= how will I assign the value here from axios response?
}
},
asyncData ({ params }) {
return axios.get(`my-url`)
.then((response) => {
return { user: response.data.data.results[0] };
});
}
}
</script>
I'm assuming that you are using NUXT (from the asyncData method).
The data you return from the asyncData method is merged into the data of your component.
You should do the following:
<template>
<v-text-field
v-model="name"
label="Name"></v-text-field>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {}
},
asyncData ({ params }) {
return axios.get(`my-url`).then((response) => {
return { name: response.data.data.results[0].name };
})
}
}
</script>
How about using the created hook?
created() {
this.name = this.user.name
}