axios call in a method with vuejs and nuxt - vue.js

For each country in my list of countries i need to make an api call with axios to get another value, here is y component :
<template>
<div>
<div v-for="(country, i) in countries" :key="i">
<div>{{ county[i.id].count }}</div>
</div>
</div>
</template>
In my script i call my method matchCount on mounted and store the value in my county data object :
<script>
export default {
props: {
countries: {
type: Array,
required: true
}
},
data() {
return {
county = {}
};
},
mounted() {
this.matchCount();
},
methods: {
matchCount() {
var paysCount = this.pays;
paysCount.forEach(item => {
this.$axios
.get(
`https://api.com/federation/}/${item.id}/`
)
.then(response => {
this.county[item.id] = {};
this.county[item.id].count = response.data.length.toString();
});
});
}
}
};
</script>
I get this error "TypeError: Cannot read property 'count' of undefined", how should i call this method ?

You will find useful using the following syntax in your HTML templates {{variable[key] && variable[key].value}}.
In your particular case it would be:
<template>
<div>
<div v-for="(country, i) in countries" :key="i">
<div>{{ county[i.id] && county[i.id].count }}</div>
</div>
</div>
</template>
What it does, is essentially verifying if the key i.id exists in county array. If not, it will not throw error about missing objects / keys.
You can use this syntax when using objects too as following:
<div v-text="house.dog && house.dog.name" ></div>
If dog is in the house object then it will show dog's name.
Edit:
Add this.$forceUpdate(); to the function:
matchCount() {
var paysCount = this.pays;
paysCount.forEach(item => {
this.$axios
.get(
`https://api.com/federation/}/${item.id}/`
)
.then(response => {
this.county[item.id] = {};
this.county[item.id].count = response.data.length.toString();
this.$forceUpdate();
});
});
}

county[item.id].count is set asynchronously, it might not be available when you render the component. You can add a safe check:
<template>
<div>
<div v-for="(country, i) in countries" :key="i">
<div v-if="county[i.id]">{{ county[i.id].count }}</div>
<div v-else>Loading...</div>
</div>
</div>
</template>
and it seems that you have reactivity problem:
this.$axios
.get(
`https://api.com/federation/}/${item.id}/`
)
.then(response => {
this.$set(this.county, item.id, {count: response.data.length.toString())
});

Related

vue js filtering with search bar

I have created an app that requests from an API the data and creates flexboxes. Now I added a search box and I would like to filter the articles by their contact and/or title.
I've also created a computed property to filter the returned list of items but when I replace in line 11 the paginated('items') with paginated('filteredArticles') that returns nothing.
What did I do wrong?
<template>
<div id="app">
<div class="search-wrapper">
<input type="text"
class="search-bar"
v-model="search"
placeholder="Search in the articles"/>
</div>
<paginate ref="paginator" class="flex-container" name="items" :list="items">
<li v-for="(item, index) in paginated('items')" :key="index" class="flex-item">
<div id="image"><img :src="item.image && item.image.file" /></div>
<div id="date">{{ item.pub_date }}</div>
<div id="title"> {{ item.title }}</div>
<div class="article">{{item.details_en}}</div>
</li>
</paginate>
<paginate-links for="items" :limit="2" :show-step-links="true"></paginate-links>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
items: [],
paginate: ["items"],
search:'',
};
},
created() {
this.loadPressRelease();
},
methods: {
loadPressRelease() {
axios
.get(`https://zbeta2.mykuwaitnet.net/backend/en/api/v2/media-center/press-release/?page_size=61&type=5`)
.then((response) => {
this.items = response.data.results;
});
},
},
computed:{
filteredArticles() {
return this.items.filter(item=>item.includes(this.search))
}
}
};
</script>
You need fields you want to search and connvert search string and fields with toLowerCase() or toUpperCase():
computed : {
filteredArticles() {
if (!this.search) return this.items
return this.items.filter(item => {
return (item.title.toLowerCase().includes(this.search.toLowerCase()) || item.contact.toLowerCase().includes(this.search.toLowerCase()));
})
}
}
Your computed doesn't seem correct. Since items is an array of objects, you'd need to do this:
filteredArticles() {
if (!this.search) {
return this.items;
}
return this.items.filter(item => {
return item.title.includes(this.search);
})
}
Note that this will only search the title field, and it's case sensitive.

how to create autocomplete component in vue js?

I am facing an issue with my autocomplete component. whenever i type anything into the input field the input is reset.I mean it does not let me type anything.It just keeps getting reset before i could fully type anything.
main.js
Vue.component('g-autocomplete', {
props: ['list','value','title'],
data() {
return {
input: '',
}
},
template: `<template>
<div class="autocomplete">
<input style="font-size: 12pt; height: 36px; width:1800px; " type="text" v-model="input" #input="handleInput"/>
<ul v-if="input" >
<li v-for="(item, i) in list" :key="i" #click="setInput(item)" >
<!-- {{ autocompleteData }} -->
<template v-if="title!='manager'">
<div class="container">
<p>
<b>ID:</b>
{{item.id}}
</p>
<p>
<b>Description:</b>
{{item.description}}
</p>
</div>
</template>
<template v-else>
<div class="container">
<p>
<b>ID:</b>
{{item.id}}
</p>
<p>
<b>First Name:</b>
{{item.firstName}}
</p>
<p>
<b>Last Name:</b>
{{item.lastName}}
</p>
</div>
</template>
</li>
</ul>
</div>
</template>`,
methods: {
handleInput(e) {
console.log('inside handleInput')
this.$emit('input', e.target.value)
},
setInput(value) {
console.log('inside setInput')
this.input = value
this.$emit('click', value)
},
},
watch: {
$props: {
immediate: true,
deep: true,
handler(newValue, oldValue) {
console.log('new value is'+newValue)
console.log('old value is'+oldValue)
console.log('value inside handler'+this.value)
console.log('list inside handler'+this.list)
console.log('title inside handler'+this.title)
this.input=this.value
}
}
// msg(newVal) {
// this.msgCopy = newVal;
// }
}
})
i reuse the above component from diffrent vue pages's like this-
<b-field label="Custom Business Unit">
<g-autocomplete v-on:input="getAsyncDataBusinessUnit" v-on:click="(option) => {updateValue(option.id,'businessUnit')}" :value="this.objectData.businessUnit" :list="dataBusinessUnit" title='businessUnit' >
</g-autocomplete>
</b-field>
my debounce function that is called when something is typed into the input field.
getAsyncDataBusinessUnit: debounce(function(name) {
if (!name.length) {
this.dataBusinessUnit = [];
return;
}
this.isFetching = true;
api
.getSearchData(this.sessionData.key,`/businessunit/?filter={id} LIKE '%25${name}%25' OR {description} LIKE '%25${name}%25'`)
.then(response => {
this.dataBusinessUnit = [];
response.forEach(item => {
this.dataBusinessUnit.push(item);
});
})
.catch(error => {
this.dataBusinessUnit = [];
throw error;
})
.finally(() => {
this.isFetching = false;
});
}, 500),
what could be the issue here ? Also i noticed that the issue doesn't happen if i comment out the body of the debounce function.So therefore i feel there is something in the debounce function that is causing this.I will try to isolate the problem but i want to understand what exactly is causing this issue. Plz help?

Vue-draggable limit list to 1 element

Here I am trying to implement cloning an element from rankings list and put it in either of the two lists (list1 & list2). Everything seems to be working, I am able to drag and put but it looks like binding does not work as the two lists are not affected, because the watchers do not run when I drag an element to a list. Also, the clone function does not print the message to the console. I was using this example as a reference.
<template>
<div>
<div>
<div>
<draggable
#change="handleChange"
:list="list1"
:group="{ name: 'fighter', pull: false, put: true }"
></draggable>
</div>
<div>
<draggable
#change="handleChange"
:list="list2"
:group="{ name: 'fighter', pull: false, put: true }
></draggable>
</div>
</div>
<div>
<div v-for="wc in rankings" :key="wc.wclass">
<Card>
<draggable :clone="clone"
:group="{ name: 'fighter', pull: 'clone', put: false }"
>
<div class="cell" v-for="(fighter, idx) in wc.fighters" :key="fighter[0]">
<div class="ranking">
{{ idx + 1 }}
</div>
<div class="name">
{{ fighter[0] }}
</div>
</div>
</draggable>
</Card>
</div>
</div>
</div>
</template>
<script>
import axios from "axios";
import draggable from "vuedraggable";
export default {
components: {
draggable
},
data() {
return {
rankings: [],
list1: [],
list2: []
};
},
methods: {
getRankingLabel(label) {
if (!label || label == "NR") return 0;
if (label.split(" ").indexOf("increased") !== -1) return 1;
if (label.split(" ").indexOf("decreased") !== -1) return -1;
},
clone({ id }) {
console.log("cloning");
return {
id: id + "-clone",
name: id
};
},
handleChange(event) {
console.log(event);
}
},
watch: {
// here I am keeping the length of both lists at 1
list1: function(val) {
console.log(val); // nothing prints, as the watcher does not run
if (val.length > 1) {
this.fighter_one.pop();
}
},
list2: function(val) {
console.log(val); // nothing prints, as the watcher does not run
if (val.length > 1) {
this.fighter_two.pop();
}
}
},
created() {
axios
.get("http://localhost:3000")
.then(res => {
this.rankings = res.data;
})
.catch(err => {
console.log(err);
});
}
};
</script>
<style>
</style>
As others have noted in the comments, your problem is likely related the <draggable> tag not containing either a :list prop or v-model.
With that said, you can limit the size of a list to 1 by calling the splice(1) method on the list in the #change event.
<draggable :list="list1" group="fighter" #change="list1.splice(1)">
{{ list1.length }}
</draggable>

this.$nextTick not working the way expected

I populate a dropdown, based on the result of another dropdown. And if i got a default value i want to set the second dropdown.
select country so i get al the country regions.
If i am on my edit page i already have the country id, so i trigger the ajax request on created() and populate the regions select.Ofc i have the region id and i would like to set it.
getRegions() {
let countryId = this.form.country_id;
if (countryId) {
axios.get('/getRegion/' + countryId)
.then(response => {
this.regions = response.data;
if (this.regions && this.form.country_region_id) {
this.$nextTick(() => {
$(".country_region").dropdown("set selected",
this.form.country_region_id).dropdown("refresh");
});
/*
setTimeout(() => {
$(".country_region").dropdown("set selected",
this.form.country_region_id).dropdown("refresh");
}, 1000);
*/
}
})
.catch(error => {
this.errors.push(error)
});
}
},
The code segment with the setTimeout is working 1sec later the correct value is selected.
Edit:
My dropdown actually got a wrapper component, so i can use v-model on it.
But the nextTick still doesnt seem to work.
<sm-dropdown v-model="form.country_region_id" class="country_region">
<input type="hidden" :value="form.country_region_id" id="country_region_id">
<div class="default text">Gebiet</div>
<i class="dropdown icon"></i>
<div class="menu">
<div v-for="region in regions" class="item" :data-value="region.country_region_id"
:key="region.country_region_id" >
{{ region.internal_name }}
</div>
</div>
</sm-dropdown>
Dropdown component:
<template>
<div class="ui fluid search selection dropdown" ref="input">
<slot></slot>
</div>
</template>
<script>
export default {
props: ['value'],
mounted() {
let self = this;
$(this.$el)
.dropdown()
.dropdown({
forceSelection: false,
onChange: function(value) {
self.$emit('input', value);
self.$emit('change');
}
}).dropdown("refresh")
;
}
}
</script>

Vuejs showing evaluation v-if before data

Ok, the question is vague, but I have a code that looks like this:
<template>
<div>
<p v-if="users" v-for="user in users"> {{ user.name}} </p>
<p v-else> No users found</p>
</div>
</template>
<script>
export default {
data() {
return {
users: null
}
},
created() {
var that = this
axios.get('...').then(response => {
that.users = response.data
}).catch(error => { .... })
}
}
</script>
So, the actuall script has no issues, it loads the users and shows it properly. But, always I see the No users founds before vuejs loads the users. I don't want to see that messages, unless users is null but it seems vue doesn't wait for that to be true before showing the v-else.
Is there any proper way to handle this
Instead of using users for the if/else, use a loading property (you would probably need it anyway to present a loading state to the user):
<template>
<div>
<p v-if="!loading && users.length" v-for="user in users"> {{ user.name}} </p>
<p v-else> No users found</p>
</div>
</template>
<script>
export default {
data() {
return {
users: null,
loading: true
}
},
created() {
var that = this
axios.get('...').then(response => {
that.users = response.data
that.loading = false
}).catch(error => {that.loading = false .... })
}
}
</script>
I think this code is better:
<template>
<div>
<p v-for="user in users"> {{ user.name}} </p>
<p v-if="isLoaded && user.length === 0"> No users found</p>
</div>
</template>
<script>
export default {
data() {
return {
isLoaded: false,
users: []
}
},
created() {
var that = this
axios.get('...').then(response => {
that.users = response.data
that.isLoaded = true
}).catch(error => { .... })
}
}
</script>