VueJS - How can i use the response of API in vue-select - api

I want to use the response coming from the API in v-select. Here is a scenario i want to use the API call from component A to component B, rather than calling it again in the component B.
Component A:
methods: {
forVselect (id) {
this.$http.get(`/type/${id}`)
.then((res) => {
this.icecream = res.data})
.catch((e) => {console.log(e)})
}
},
mounted (){
this.forVselect (this.$route.params.un_id)
}
Component B:
<template>
<div class="The V-select">
<vselect v-model="input1" :options="[{label: 'Vanilla' , value: 'vanilla'}]" :dir="$vs.rtl ? 'rtl' : 'ltr'" />
</div>
</template>
<script>
import vselect from 'vue-select'
...
input1: null,
icecream: null
...
methods: {
forVselect (id) {
this.$http.get(`/type/${id}`)
.then((res) => {
this.icecream = res.data})
.catch((e) => {console.log(e)})
}
},
mounted (){
this.forVselect (this.$route.params.un_id)
}
</script>
As you can see my Component B i have hard coded as 'vanilla' in v-select, rather i want to use the data coming from the API, i want to know how it can be done.
Here is my Api response:
[
{
"id": 3,
"flavor": "vanilla",
"price": "150",
"taste": "super",
"cream": "high",
"investments": null,
},
{
"id": 8,
"flavor": "chocolate",
"price": "250",
"taste": "super high",
"cream": "fantastic",
"investments": "too high",
}
...
]
Please do help me. I tried just by using label: type.flavor but nothing was displayed. And in order to make code effective i want to use the response coming from API call made in component A

use just have to add a variable at the place of option as shown below:
<template>
<div class="The V-select">
<vselect v-model="input1" :options="icecream" :dir="$vs.rtl ? 'rtl' : 'ltr'" />
</div>
</template>
<script>
import vselect from 'vue-select'
...
input1: null,
icecream: null
...
methods: {
forVselect (id) {
this.$http.get(`/type/${id}`)
.then((res) => {
this.icecream = res.data})
.catch((e) => {console.log(e)})
}
},
mounted (){
this.forVselect (this.$route.params.un_id)
}
</script>
and also you need modify your api response... response like:
[
{
"id": 3,
"flavor": "vanilla",
"price": "150",
"taste": "super",
"cream": "high",
"investments": null,
"label": "Vanilla" ,
"value": "vanilla"
},
{
"id": 8,
"flavor": "chocolate",
"price": "250",
"taste": "super high",
"cream": "fantastic",
"investments": "too high",
"label": "Chocolate" ,
"value": "chocolate"
}
...
]
you need to modify response like that from server side or client side when response received...
If you don't want to modify your json response so atleat you need to add 2 additional key which is label & value key so that you can use...

I tried to use :getOptionKey="getOptionKey" so I could change the default "id" vue-select was requesting but to me the only think that worked is to consider the object attribute "value" as the default.
So since I was working with array of objects being returned from API, what I did was:
// loading from API
dataUtils.find(this.$route.params.id).then((data) => {
this.mySelectObject = {
name: data.name,
value: data.id
}
and used the following within html:
<v-select
label="name"
:options="myOptions"
v-model="mySelectObject"
#input="setSelected" //created this method to change selection
/>

Related

Using VueUse composables with the component options api

Is possible to use VueUse with the Options API style? All examples are using Composition one.
I try to use computedAsync. The stock function returns a Promise<VendorItem[]>.
Using Composition API as the example works fine :
const vendors = computedAsync(
async () => {
return await stock(db);
},
[],
);
But this does not work when I try to use it using the Options API style:
export default defineComponent({
name: "VendorsList",
computed: {
vendors() {
return computedAsync(
async () => {
return await stock(db);
},
[],
)
}
}
})
In the last case, vendors does not contain the array.
I try to debug it adding:
<div v-for="vendor in vendors" class="">
<pre>{{ JSON.stringify(vendor, null, 4)}}</pre>
---------------
</div>
and the output is something like this
false
---------------
---------------
true
---------------
[
{
"id": "00PIL9ljDrgRZrP8MOkl",
"language": "en"
},
{
"id": "00ZnoY0NXb1ZjQMFQJCC",
"language": "en"
},
{
"id": "01hO4DKjAprwP8zwUpZU",
"language": "en"
}
]
---------------
[
{
"id": "00PIL9ljDrgRZrP8MOkl",
"language": "en"
},
{
"id": "00ZnoY0NXb1ZjQMFQJCC",
"language": "en"
},
{
"id": "01hO4DKjAprwP8zwUpZU",
"language": "en"
}
]
---------------
Simple anwser is no, you can't use vue composables (including VueUse ones) in the option api except for the setup function. Source in the vue documentation.
The computedAsync composable already creates a computed, so here you are trying to create a computed of a computed. Just move it in the setup function and it will work.
export default defineComponent({
name: "VendorsList",
setup() {
const vendors = computedAsync(async () => await stock(db), [])
return { vendors }
},
})

TypeError: $data.quotation.company is undefined

I have problem when I try to render data in my Vue3 application.
data() {
return {
quotation: [],
}
},
mounted() {
this.getQuotation()
},
methods: {
async getQuotation() {
this.$store.commit('setIsLoading', true)
const quotationID = this.$route.params.id
await axios
.get(`/api/v1/quotations/${quotationID}/`)
.then((response) => {
this.quotation = response.data
})
.catch(error => {
console.log(error)
})
},
}
The weird part is when I try to access {{quotation.company}} in template I can see the element of "company" without any error. The error TypeError: $data.quotation.company is undefined occurs when I get in depth {{quotation.company.name}} for example.
Axios is getting data like:
{
"id": 20,
"company": {
"id": 4,
"name": "xxxx",
"slug": "xxx",
"categories": [
{
"id": 7,
"name": "xxxx",
"slug": "xxxx"
}
],
"street2": "",
"postcode": 11111,
},
"home_type": "xxxx",
"Urgency": "",
"description": "xxxx",
}
I really don't understand :/
First the quotation property should be declared as an object like quotation: {}, then at the first rendering the field company is not available yet, so you need to add some conditional rendering as follows :
<div v-if="quotation.company" >
{{quotation.company.name}
</div>

Vue FullCalendar: Date and Event doubleclick handler

I'm using Vue FullCalendar 5.3.1. I want to add event on doubleclick on empty date cell and edit event on doubleclick on event. How can I implement this? There are 2 methods by default: dateClick() and eventClick() and it's works fine for me.
My code:
<template>
<div>
<heading class="mb-6">Scheduler</heading>
<card class="custom-card">
<FullCalendar :options="calendarOptions"/>
</card>
</div>
</template>
<script>
import FullCalendar from '#fullcalendar/vue'
import dayGridPlugin from '#fullcalendar/daygrid'
import interactionPlugin from '#fullcalendar/interaction'
import resourceTimelineDay from '#fullcalendar/resource-timeline'
export default {
components: {
FullCalendar // make the <FullCalendar> tag available
},
data() {
return {
calendarOptions: {
dateClick: function(info) {
console.log(info.dateStr)
console.log(info.resource.id)
},
eventClick: function(info) {
console.log(info)
},
height: 250,
plugins: [ dayGridPlugin, interactionPlugin, resourceTimelineDay ],
headerToolbar: {
left: 'today prev,next',
center: 'title',
right: 'resourceTimelineDay,resourceTimelineWeek'
},
initialView: 'resourceTimelineDay',
aspectRatio: 1.5,
editable: true,
resourceAreaColumns: [
{
field: 'title',
headerContent: 'Worker'
}
],
resources: [
{
"id": "worker_a",
"title": "Worker A"
}, {
"id": "worker_b",
"title": "Worker B",
"eventColor": "green"
}, {
"id": "worker_c",
"title": "Worker C",
"eventColor": "orange"
}
],
events: [{
"resourceId": "worker_a",
"title": "Job 5",
"start": "2020-09-15T10:00:00+00:00",
"end": "2020-09-15T15:00:00+00:00"
}, {
"resourceId": "worker_b",
"title": "Job 2",
"start": "2020-09-15T09:00:00+00:00",
"end": "2020-09-15T14:00:00+00:00"
}, {
"resourceId": "worker_b",
"title": "Job 4",
"start": "2020-09-15T15:30:00+00:00",
"end": "2020-09-15T17:30:00+00:00"
},
]
}
}
}
}
</script>
BTW as I noticed that now all calendar settings are passed through :options = "". If you want to pass events like so <FullCalendar :events="events"/> or handle an event like <FullCalendar #dateClick="dateClick"/>, you cannot do this. Everything needs to be passed in the calendarOptions object (documentation)
The fullcalendar doesn't provide this option.
But you can attach the double click handler, when the event object is contructed by using an Event Render Hooks
In the version 5 we can use the funcion eventDidMount
data() {
return {
calendarOptions: {
...
eventDidMount: function(eventInfo){
// Not mandatory, but you can set an id to the object
eventInfo.el.id = eventInfo.event.id;
// Set the dbclick event
eventInfo.el.ondblclick = function(){
console.log(eventInfo.event)
};
}
}
}
}
Note: This works, only because this function is called only one time, in case that you are working in other version check how many times the function is called.

Axios returns a string instead of JSON (vue.js)

I don't understand why but axios is returning a string instead of json. Can someone explain me why ?
<template>
<div class="app">
<Header/>
<h1>{{services}}</h1>
<Services v-bind:services="services"></Services>
</div>
</template>
<script>
import Header from "./components/Header.vue";
import Services from "#/components/Service";
import axios from 'axios';
export default {
name: 'App',
components: {
Services,
Header,
},
data() {
return {
services: [],
}
},
created() {
const instance = axios.create({
baseURL: 'http://localhost:3000/api',
timeout: 1000,
headers: {'Authorization': 'Bearer ' + 'mysecretcode'}
});
instance.get('/service')
.then(response => {
this.services = response.data;
console.log(response.data);
})
.catch(e => {
this.errors.push(e)
})
},
}
</script>
<style>
</style>
I saw online that response.data is supposed to send back only the parsed json data but on my {{services}} I get this :
{ "status": 1, "message": "Operation success", "data": [ { "_id": "5edfdaf5586d4c75036bc853", "title": "Logo rapide", "description": "testing service desc", "createdAt": "2020-06-09T18:54:45.904Z" }, { "_id": "5edfdafd586d4c75036bc854", "title": "Logo rapide", "description": "testing service desc", "createdAt": "2020-06-09T18:54:53.054Z" }, { "_id": "5edfdc8bc07c7677915275c1", "title": "Logo rapide", "description": "testing service desc", "createdAt": "2020-06-09T19:01:31.945Z" }, { "_id": "5edfdc8cc07c7677915275c2", "title": "Logo rapide", "description": "testing service desc", "createdAt": "2020-06-09T19:01:32.621Z" } ] }
instead of the parsed data.
Thank you :)
If the response is a string then you could use:
this.services = JSON.parse(response.data).data
else if it is a JSON object already (I think it might be - but get the actual data object from your response.data):
this.services = response.data.data
Then you could use v-for and get the title with {{service.title}}
Hope it helps.
There might be an error in JSON. Axios return string when it fails to parse data into JSON.
The common error in JSON is missing quotes in param names. Compare:
JS: {x:"y"}
JSON: {"x":"y"}
This has to do with invalid JSON from the server side. You can use an online JSON validator like https://jsonlint.com/ to validate the JSON response.

Filter on nested (recursive) data ( Vue 2 )

Here is an example of my JSON data :
"data":[
{
"id":01,
"name":"test",
"parent_id":null,
"children":[
{
"id":15,
"name":"subChild",
"parent_id":21,
"children":[
{
"id":148,
"name":"subSubChild",
"parent_id":22,
"children":[
....
]
}
]
}
]
},
I would like to filter this level by level. I have made this method :
computed: {
filteredData: function () {
let filterData = this.filter.toLowerCase()
return _.pickBy(this.data, (value, key) => {
return _.startsWith(value.name.toLowerCase(), filterData)
})
},
This work for only the first "level" and I tried several solutions but none worked for children.
So, I would like to be able to filter by several levels.
If you have an idea!
Thank you
A recursive function could come in handy for this particular purpose.
Try the following approach, and for better view, click on Full page link next to the Run code snippet button down below.
new Vue({
el: '#app',
data() {
return {
filter: '',
maintainStructure: false,
data: [{
"id": 01,
"name": "test",
"parent_id": null,
"children": [{
"id": 15,
"name": "subChild",
"parent_id": 21,
"children": [
{
"id": 148,
"name": "subSubChild",
"parent_id": 22,
"children": []
},
{
"id": 150,
"name": "subSubChild3",
"parent_id": 24,
"children": []
}
]
}]
}]
}
},
methods: {
$_find(items, predicate) {
let matches = [];
for (let item of items) {
if (predicate(item)) {
matches.push(item);
}
else if (item.children.length) {
let subMatches = this.$_find(item.children, predicate);
if (subMatches.length) {
if (this.maintainStructure) {
matches.push({
...item,
children: subMatches
});
}
else {
matches.push(subMatches);
}
}
}
}
return matches;
},
filterBy(item) {
return item.name.toLowerCase().startsWith(this.filter.toLowerCase());
}
},
computed: {
filteredData() {
return this.$_find(this.data, this.filterBy);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
<label>Filter by <code>item.name</code>:</label>
<input v-model.trim="filter" placeholder="e.g. subsub" />
</div>
<div>
<label>
<input type="checkbox" v-model="maintainStructure" /> Maintain structure
</label>
</div>
<hr />
<pre>{{filteredData}}</pre>
</div>
Note that I'm prefixing the function with $_ to sort of mark it as private function (as recommended in this Style Guide) since we're not going to invoke it anywhere else.