VueJS in-line template and v-for in it - vue.js

I'm trying to use in-line template that I have for dropdown menus but it's messing up the things upon switching between states.
This is the code I use in the main template:
<div>
<article v-for="(game, index) in games">
<dropdown ref="dropdown" inline-template>
<a v-for="(branch, index) in game.branches" :key="'branch' + index" :href="branch.link">{{ branch.name }}</a>
</dropdown>
</article>
</div>
It's working fine on load, but when I switch between categories the links are messing up, and the same links from the load stay for the other renders.
Am I doing something wrong?

You are missing to set an unique :key on the a tag.
The key, needs to be a unique identifier for the looped element. In the example below I'm using the index of the current looped element, but thats just for the sake of the example. Usually is not considered good practice to use the index because it can change forcing elements to re - render.
You will note that the name of the var holding the indexes in the v-for are different:
the indexes var needs to have unique names too, or the index in the inner loop will "steal" the index from the outer loop.
<article v-for="(game, gIndex) in games" :key="gIndex">
<dropdown ref="dropdown" inline-template>
<a v-for="(branch, bIndex) in branches"
:key="gIndex+bIndex"
:href="branch.link">
{{ branch.name }}
</a>
</dropdown>
</article>

Have a look here.
In 2.2.0+, when using v-for with a component, a key is now required.
For that reason you should be using :key in v-for.
You can assign your key to index or make it even more unique like:
:key="'branch'+index"
in case if you used similar keys before in your component (key should be unique).

Related

How to use `v-if` and `v-for` on the same element?

Hi I'm trying to figure out how to use v-if on a iterated element which also uses v-for. I need to check if the current element has any of a series of classes, which are numbers.
so the classes of each article would be:
<article class="post-item post-guide 12 22 19 30 55">...
this is the HTML that renders all:
<article v-if="showIfHasClass" :class="'post-item post-guide ' + guide.categories.toString().replace(/,/g, ' ')"
v-for="(guide, index) in guides" :key="index">
<header>
<h1 class="post-title">
{{ guide.title.rendered}}
</h1>
</header>
</article>
I have tried with methods that check the class of all elements, that works, but i'm trying to use a clean Vue built-in solution with v-if without success, i'm not able to retrieve the class of this in a successful way.
Should showIfHasClass be a computed property? I have tried with that too... but it seems, I'm missing something along the way.
my data I have to check against is an array:
data:{
guides: [...]
selectedCategories: [1, 22, 33, 100, 30];
}
or maybe it is better to directly loop over the guides and check if they have the selectedCategory or not, then remove the element from the guides data array?
What is more effective?
Besides the option to create an additional filtered computed (effectively eliminating the need to use v-for and v-if on the same element), you also have a template level way of dealing with such edge-cases: the <template> tag.
The <template> tag allows you to use arbitrary template logic without actually rendering an extra element. Just remember that, because it doesn't render any element, you have to place the keys from the v-for on the actual elements, like this:
<template v-for="(guide, index) in guides">
<article v-if="isGuideVisible(guide)"
:key="index"
class="post-item post-guide"
:class="[guide.categories.toString().replace(/,/g, ' ')]">
<header>
<h1 v-text="guide.title.rendered" />
</header>
</article>
</template>
isGuideVisible should be a method returning whether the item is rendered, so you don't have to write that logic inside your markup. One advantage of this method is that you can follow your v-if element with a fallback v-else element, should you want to replace the missing items with fallback content. Just remember to also :key="index" the fallback element as well.
Apart from the above use-case, <template> tags come in handy when rendering additional wrapper elements is not an option (would result in invalid HTML markup) (i.e: table > tr > td relations or ol/ul > li relations).
It's mentioned here as "invisible wrapper", but it doesn't have a dedicated section in the docs.
Side note: since you haven't actually shown what's inside guide.categories, I can't advise on it, but there's probably a cleaner way to deal with it than .toString().replace(). If guide.categories is an array of strings, you could simply go: :class="guide.categories".
I think the most Vue way is to create a computed property with filtered items from selected categories, then use that in v-for loop (the idea is to move the business logic away from template).
computed: {
filteredItems(){
return this.guides.filter(e => this.selectedCategories.includes(e.category))
}
}
Also, as a note, it is not recommended to use v-if and v-for on the same element, as it may interfere with the rendering and ordering of loop elements. If you don't want to add another level of nesting, you can loop on a 'template' element.
<template v-for="item in items">
// Note the key is defined on real element, and not on template
<item-element v-if='condition' :key="item.key"></item-element>
</template>

vue multiple tag 'template' in one single file component

I am work in company with big frontend team, and guys use multiple template tag in single file components. Before that I never see something like this, for me it bad practice. But head developers think that I am stuped, when I ask about that.
Can some one please explain me, when I must use it and why? and if it possible please give link to vue documentation.
And yes, we use vuetify.
example:
<template>
<VContainer>
<VRow>
<VCol>
<h2>
{{ title }}
</h2>
<p>
{{ subtitle }}
</p>
</VCol>
</VRow>
<Share />
<template v-if="p.length > 0">
<VRow>
<VCol>
{{ text }}
</VCol>
</VRow>
<VDivider/>
</template>
<template v-for="(t, index) in ts">
<VRow :key="index">
<VCol v-if="t.p.length > 0">
{{ text }}
</VCol>
</VRow>
<VDivider
v-if="index < t.length - 1"
:key="`divider-${index}`"
class="mx-3"
/>
</template>
</VContainer>
</template>
The <template> used here is just a way to handle loops or conditionals without inserting extra nodes into the DOM.
You could put the v-if or v-for directly on the <VRow> instead of on a <template> that wraps it, but sometimes that's undesirable -- if there are already other conditions there that you want to keep separate, or if you want to wrap multiple nodes in the same condition, as in your example where you have both a <VRow> and a <VDivider> contained in a single <template>.
It's not bad practice and has no undesirable performance effect at all. Your head developers should be better able to communicate that to you rather than calling you 'stupid'.
I think it does't matter use multiple template, We should not use div wrapper the condition render Component, the div will insert to DOM.
here is the official documemnt https://v2.vuejs.org/v2/guide/conditional.html#Conditional-Groups-with-v-if-on-lt-template-gt

Accessing the index variable in a nested v-for loop in vue

I need to access the index variable of a nested v-for loop. Can this be done? My code is
<div v-for="(dayDataArray, key, index) in props_league_data_nfl">
<div v-for="(arrayItem, key, arrayItemIndex) in dayDataArray" class="col-xs-12 col-sm-4 col-lg-3">
<!-- some code here -->
<div> {{ props_box_game_scores_nfl[nfl_days[index].split(' ')[0].toLowerCase()][arrayItemIndex] }} </div>`
If I set it up like above with separate names for indexes then arrayItemIndex seems to be ignored. If I use index for both for loops then I get an exception in the nfl_days[index] reference. How can you access the second index variable? It seems like the index name has to be used in both instances? Any suggestions or workarounds appreciated...
For an array v-for only has two arguments, not three. So it should be:
v-for="(arrayItem, arrayItemIndex) in dayDataArray"
It only has 3 arguments when iterating over an object.

vuejs expression is not working correctly

what is wrong with my expressions? I am making a rest api call, which works fine. Data loads and gets written to console. I am simply trying to get that data on screen. I can but its not correct. It either shows all of the data. I am only trying to get the first item in the array. items.Name does not work, but it does if I do a v-for="item in items". Regardless it loads everything. When I add a [0] to get the first element, it simply removes all the text except the first letter of the object in the array.What do I need to do?
<p>{{items.Name}}</p>
Please, read the docs about list rendering
https://v2.vuejs.org/v2/guide/list.html
If you have an items array of objects, then you can loop over it in your HTML by using v-for, or directly access each entry by specifying the index in the expression: {{ items[0].Name }}
<ul>
<li v-for="item in items" :key="item.ID">{{ item.name }}</li>
</ul>
Notice you're using item, not items, inside the <li> tag, because you instructed v-for to assign the cursor to that variable.

How to deal with v-for in vuejs when element has no id?

I often get this error related to the v-for directive.
Elements in iteration expect to have v-bind:key directive
When using a for loop like this.
<div v-for='person in people'> {{ person.name }} </div>
The problem is, in sometimes in rare cases, I have no id key for person. And I want to know what can be done in this case.
As Ghanshyam already mentioned in comments, you can easily generate an index in v-for.
<div v-for="(person, index) in people" :key="index" >
{{ person.name }}
</div>
However, there is something to be said for NOT using index as a key.
Other SO Post: Why not always use the index as the key in a vue.js for loop?