how to make infinite nested loop - vuejs2

i have a multiple array into array could infinite like
['level1', ['level2', ['level3', ['level4']]]]
i really don't understand how can i use v-for loop in vue2
<template>
<ul>
<li v-for="downline in downlines" :key="downline.id">
{{ downline.name }}
</li>
</ul>
</template>
<script>
export default {
data(){
return {
downlines: [] //<-- infinite array
}
}
}
</script>

If you trying to make infinite v-for you got this error ...
What you exactly need is v-for in nested array (I recomend to you rename your question), and in this post you can find your answer

Related

Access current component data from within slot template

I have the following vue component
<template>
<CardGroup>
<template #headerRight>
<div>Total items: {{ this.total }}</div>
</template>
</CardGroup>
</template>
export default {
data() {
return {
total: 0
};
}
}
I don't understand the scoping problem. The this in the slot template is null and I cannot access the this.total data property. I can use that property outside the slot template though.
Why this is null inside the slot template?
Vue binds properties automatically, please go through it.
data binding
<div>Total items: {{ total }}</div>
Well, the solution was somewhat simple. I just had to omit this
<div>Total items: {{ total }}</div>
It turns out vue binds properties automatically to the _vm.

How use v-if with array length

I want to show a div if the array length is more than 0. I used it below.
HTML:
<div v-if="countfunc > 0">
<div v-for="nhp in countfunc">
<p v-text="nhp.code"></p>
</div>
</div>
Vue:
computed: {
countfunc: function(){
//this return a js array
}
}
How can I solve my problem?
You can use it as countfunc.length>0
The thing is, if countfunc is an empty array it will not show anything anyway, so this double check is a little bit unnecessary. Of course in your code you're forgetting the :key property in v-for as well.
Here you can try on codepen, try to delete the object inside the array and let it empty like
<script>
export default {
data() {
return {
items: [],
};
},
};
</script>
And you can see that not will be rendered since the array is empty.

why v-for on array of objects within an object, doesn't update even with :key?

I'm iterating through an array within an object. When I update an object in the array, the data updates, but, the UI doesn't
Data looks like this
projectConfig = {
files: [
lang: "ar-eg",
paths: ["/Users/omarabdelhady/Desktop/Others/test/i18n/ar-eg.json"]
]
}
HTML
<div class="col-12 files-box">
<div v-for="(file, index) in projectConfig.files" :key="file.lang">
<bdi>Lang : {{file.lang}}</bdi>
<div>
Files :
<ul class="col-12 app-list">
<li v-for="(path, i) in file.paths" :key="path">
{{getFileNameFromPath(path)}}
<i class="i-bin"
#click="deleteLang(i, index)">
</i>
</li>
</ul>
</div>
</div>
</div>
delete method
deleteLang(pathIndex, fileIndex) {
this.projectConfig.files[fileIndex].paths.splice(pathIndex, 1);
if(this.projectConfig.files[fileIndex].paths.length === 0) {
this.projectConfig.files.splice(fileIndex, 1);
}
}
I use the :key for v-for to detect changes, didn't work
I tried this.$forceUpdate(), worked, but I think it is not the best solution
-- update
actually the data looks like this
projectConfig = {
files: [{
lang: "ar-eg",
paths: ["/Users/omarabdelhady/Desktop/Others/test/i18n/ar-eg.json"]
}]
}
plus I'm using typescript
I'm guessing that the problem is that the reactivity is not setup on component initialization. Either the data is not available initially, or there are too many nested objects and arrays.
looking at this nested object...
projectConfig.files[i].paths[j]
that is a string in an array in an object in an array in an object.
if the array is empty when the component is initiated, the reactivity is not added. You can try using slice and $set to trigger the reactivity.

VueJS: v-model on <span> element ? How to handle that?

I have this simple component displaying user info:
<div class="m-card-user__details">
<span class="m-card-user__name m--font-weight-500">
{{ firstname }} {{ lastname }}
</span>
<a class="m-card-user__email m--font-weight-300 m-link">
{{ loginEmail }}
</a>
</div>
Script
<script>
export default {
data () {
return {
loginEmail : this.$store.getters.user.loginEmail,
firstname: this.$store.getters.user.firstName,
lastname: this.$store.getters.user.lastName,
}
}
};
</script>
Problem is that if another component change the value of the firstname property in the VueX store, I see that the value is well updated on the store but not on my component here..
How can i set the 2 ways bindings on a element ?
Attaching a store variable directly to data() will break the 2-way-binding.
Use a computed property for that, like:
computed: {
loginEmail() {
return this.$store.getters.user.loginEmail;
}
}
and then use the computed on the span, like {{ loginEmail }}, as you would normally.
Improvement: If you want, you can return the entire ...getters.user (as a object) with
computed, like:
computed: {
user() {
return this.$store.getters.user;
}
}
and then use it on your span like {{ user.loginEmail }} and so on.
This will save you some lines, increase readability and possibly a tiny bit of performance.
You can also directly use $store in your template.
<div class="m-card-user__details">
<span class="m-card-user__name m--font-weight-500">
{{ $store.getters.user.firstName }} {{ $store.getters.user.lastName }}
</span>
<a class="m-card-user__email m--font-weight-300 m-link">
{{ $store.getters.user.loginEmail }}
</a>
</div>
I haven't tried if this works with getters, but I don't see a reason why it wouldn't. Now you could maybe argue it's an anti-pattern, but I'd prefer it over having a computed property solely for this purpose.
See also https://github.com/vuejs/vuex/issues/306.
Edit: Because you mention 2-way binding: You shouldn't do this to update the $store, use actions and mutations instead. In this case, it's fine as it's essentially a 1-way binding where the state flows to the innerHTML of your <span>s.

Getting ref of component in async v-for

I have a list of items that don't get created until after an async call happens. I need to be able to get the getBoundingClientRect() of the first (or any) of the created items.
Take this code for instance:
<template>
<div v-if="loaded">
<div ref="myItems">
<div v-for="item in items">
<div>{{ item.name }}</div>
</div>
</div>
</div>
<div v-else>
Loading...
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
items: []
}
},
created() {
axios.get('/url/with/some/data.json').then((response) => {
this.items = response.data;
this.loaded = true;
}, (error) => {
console.log('unable to load items');
});
},
mounted() {
// $refs is empty here
console.log(this.$refs);
// this.$refs.myItems is undefined
}
};
</script>
So, I'm trying to access the myItems ref in the mounted() method, but the this.$refs is empty {} at this point. So, therefore, I tried using a component watch, and various other methods to determine when I can read the ref value, but have been unsuccessful.
Anyone able to lead me in the right direction?
As always, thanks again!!
UPDATES
Added a this.$watch in the mounted() method and the $refs still come back as {}. I then added the updated() method to the code, then was able to access $refs there and it seemed to work. But, I don't know if this is the correct solution?
How does vuejs normally handle something like dynamically moving a div to an on-screen position based on async data? This is similar to what I'm trying to do, grab an element on screen once it has been rendered first (if it even should be rendered at all based on the async data), then access it to do something with it (move it to a position)?
Instead of doing on this.$refs.myItems during mounted, you can do it after the axios promise returns the the response.
you also update items and loaded, sou if you want to use watch, you can use those
A little late, maybe it helps someone.
The problem is, you're using v-if, which means the element with ref="myItems" doesn't exist yet. In your code this only happens when Axios resolves i.e. this.loaded.
A better approach would be to use a v-show.
<template>
<div>
<div v-show="loaded">
<div ref="myItems">
<div v-if="loaded">
<div v-for="item in items">
<div>{{ item.name }}</div>
</div>
</div>
</div>
</div>
<div v-show="!loaded">
Loading...
</div>
</div>
</template>
The difference is that an element with v-show will always be rendered and remain in the DOM; v-show only toggles the display CSS property of the element.
https://v2.vuejs.org/v2/guide/conditional.html#v-show