Vuejs showing evaluation v-if before data - vue.js

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>

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.

can't display vuejs data property inside vue template

I'm trying to use eventbus to send data from component A:
<template>
<div v-for="(user, index) in users" :key="index" class="col-lg-6">
<div class="card card-primary card-outline">
<div class="card-body d-flex">
<h1 class="mr-auto">{{ user.name }}</h1>
Afficher
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
users: {},
}
},
methods: {
envoyerDetails($data){
Fire.$emit('envoyer_details_projet', $data);
this.$router.push('details-projet');
},
loadUser() {
if(this.$gate.isAdmin()){
axios.get("api/user").then(({ data }) => (this.users = data.data));
}
}
},
mounted() {
this.loadUser()
}
}
</script>
In component B, i receive the data and i want to display it inside the template this way:
<template>
<div class="right_col text-center" role="main">
<h5><b>name: {{ user.name }}</b> </h5>
</div>
</template>
export default {
data() {
return {
user: {},
}
},
methods: {
afficherDetails (args) {
this.user = args;
console.log(this.user.name);
}
},
mounted() {
Fire.$on('envoyer_details_projet', this.afficheDetails);
}
}
The data is not displayed in the template but it is displayed in the console. What am i missing?
Maybe when you emit the event envoyer_details_projet in component A, but component B is not mounted yet so that it can't receive the data.

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?

How to had a conditional render with a v-if in nuxt.js

I have an application that displays companies and their documents.
I would like to display a main title if no application is selected, and display the content of the application and removing the main title if an application is selected.
<template>
<div class="container">
<div v-if="docsAppDisplayed === false">
<Logo />
<h1 class="title">
<em class="text-red-900 font-bold">D</em>ocs<em class="text-yellow-700 font-bold">C</em>loud<em class="text-purple-900 font-bold">M</em>anager
</h1>
</div>
<div>
<DocumentationCard />
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
data () {
return {
docsAppDisplayed: false
}
},
computed: {
...mapGetters(['applications', 'selectedApp', 'documentations', 'selectedDoc'])
},
async mounted () {
await this.$store.dispatch('getApplications', 'getDocumentations')
},
methods: {
selectApplications (id) {
this.$store.dispatch('selectedApp', id)
this.docsAppDisplayed = true
}
}
}
</script>
I don't know why my method doesn't work. An idea, please?
I specify that I have a sidebar that returns my company names and that when I click on it I can access their documents. My problem only concerns the display of the (logo + title) or my DocumentCard.
This never seems to get invoked by your component.
selectApplications (id) {
this.$store.dispatch('selectedApp', id)
this.docsAppDisplayed = true
}
Where is id coming from? Is it a route parameter, like https://example.com/myroute/1234 where 1234 is the id? Without knowing much about your code, my guess is, you're looking to do something like this:
export default {
data () {
return {
docsAppDisplayed: false
}
},
computed: {
...mapGetters(['applications', 'selectedApp', 'documentations', 'selectedDoc'])
},
async mounted () {
await this.$store.dispatch('getApplications', 'getDocumentations')
selectApplications(this.$route.params.id) // THE BIG CHANGE
},
methods: {
selectApplications (id) {
this.$store.dispatch('selectedApp', id)
this.docsAppDisplayed = true
}
}
}
You also might want to add a v-else to the div wrapping <DocumentationCard />.
<template>
<div class="container">
<div v-if="!docsAppDisplayed">
<Logo />
<h1 class="title">
<em class="text-red-900 font-bold">D</em>ocs<em class="text-yellow-700 font-bold">C</em>loud<em class="text-purple-900 font-bold">M</em>anager
</h1>
</div>
<div v-else>
<DocumentationCard />
</div>
</div>
</template>

axios call in a method with vuejs and nuxt

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())
});