Vue.js v-for item, bind attribute to current index / key - javascript

<li v-for="(value, key) in {facebook: [data], twitter: [data]}">
<i class="icon" :class="key"></i>
....
</li>
Error: [Vue warn]: Trying to access non-existent property "key" while rendering.
How can I bind key in attributes of li's children? It only works in li itself.

It works as expected for me. If you inspect the items in the snippet below, you'll see the class is assigned.
new Vue({el: 'body'});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<li v-for="(key, value) in {facebook: ['data'], twitter: ['data']}">
<i class="icon" :class="key">{{key}}</i>
</li>

Related

Template loop gives duplicate keys warning

<template v-for="errors in validationErrors">
<li v-for="(error, index) in errors" :key="index">{{ error }}</li>
</template>
The above code throws:
Duplicate keys detected: '0'. This may cause an update error
If I remove index from the inner loop and assign error to key then there is no warning, however that just seems wrong.
Any way to not have this warning when using templates?
You could use this instead:
<template v-for="(errors, outerIndex) in validationErrors">
<li v-for="(error, index) in errors" :key="outerIndex + '-' + index">
{{ error }}
</li>
</template>
Explanation
Without unique information from the outer loop, the inner loop key will use the same set every time, so you end up with duplicates. Ex. if you had two outer loops with 3 items each, you'd get this:
<li key="0">...</li>
<li key="1">...</li>
<li key="2">...</li>
<li key="0">...</li>
<li key="1">...</li>
<li key="2">...</li>
By using an outerIndex too, you maintain uniqueness in the inner elements:
<li key="0-0">...</li>
<li key="0-1">...</li>
<li key="0-2">...</li>
<li key="1-0">...</li>
<li key="1-1">...</li>
<li key="1-2">...</li>

Append CSS class to element

I have a model, i.e. like this:
[{
"Name" : "Foo",
"CssClass" : "class1"
},
{
"Name" : "Bar",
"CssClass" : "class2"
}]
Which is presented using the following template:
<li v-for="foobar in model">
<span class="otherclass anotherclass">{{foobar.Name}}</span>
</li>
How could I append the CssClass property to the span?
I know you could do :class="{ active: isActive }" (as per the documentation), but this uses a predefined class called active, whereas I want to append the class name from the model.
I tried using <span class="otherclass anotherclass" :class="foobar.CssClass"> but that didn't add CssClass to class at all.
How could I go about making this work so that the <span> will effectively be rendered as <span class="otherclass anotherclass class1"> (for the first model entry)? And how could I also use a default value in case CssClass is not defined?
You can append the class like so
<li v-for="foobar in model">
<span :class="foobar.CssClass" class="otherclass anotherclass">{{foobar.Name}}</span>
</li>
I ran into the same issue in the past. During render all three classes will combine into one class="my_class_from_foobar otherclass anotherclass"
You can pass an object or an array to :class. You can also use class and :class together and Vue will resolve both correctly without issues:
<li v-for="foobar in model">
<span class="otherclass anotherclass" :class="[foobar.CssClass]">{{foobar.Name}}</span>
</li>
<li v-for="foobar in model">
<span :class="'otherclass anotherclass ' + foobar.CssClass">{{foobar.Name}</span>
</li>
<li v-for="foobar in model">
<span :class="foobar.CssClass" class="otherclass anotherclass">{{foobar.Name}</span>
</li>
Both should work

How to get attribute value inside li tag?

I am using angular 6 application where i am passing data through attribute,
First i have given [attr.parent_id] in ul tag like,
<ul class="list-unstyled" id="1" [attr.parent_id]="123"></ul>
For which i have used, console.log(target.getAttribute('parent_id'));
And the result displays 123.
Like same thing i need to get a index number from li tag like of the same ul,
<ul class="list-unstyled" id="1" [attr.parent_id]="123">
<li class="media p-2 column" *ngFor="let item of items;let i = index;" [attr.child_id]="i"></li>
</ul>
Here i have given [attr.child_id] to retrieve the index position and i have used console.log(target.getAttribute('child_id'));.
This console of getting child_id displays the value as null.
The ul tag gives me the value of attribute but li tag is returning null as result.
The StackBlitz link was https://stackblitz.com/edit/angular-oaghq3
Kindly help me to set and get the attribute to li tag and to retrieve data.
You can query the elements by using the ViewChildren decorator as showcased in this blitz
<ul class="list-unstyled" id="list" [attr.parent_id]="123">
<li #li class="media p-2 column" *ngFor="let item of items;let i = index;" [attr.child_id]="i"> {{item.name}} </li>
</ul>
#ViewChildren("li") listElements: QueryList<ElementRef<HTMLLIElement>>;
private printElements() {
const elements = this.listElements.toArray();
elements.forEach(element => {
const isLiElemenet = element.nativeElement instanceof HTMLLIElement;
const child_id = element.nativeElement.getAttribute('child_id');
console.log({ isLiElemenet, child_id });
})
}

how to identify specific list element field and set its displaying objects's property to true

So using *ngFor i displayed the list of name property of object, this object also has a property named isAvailable I want to set isAvalable property to toggle between true and false when I click on it, and based on isAvalible a text next to li will display is available or not
<p>Authors</p>
<ul>
<li *ngFor='let author of authors' (click)='onClick()'>
{{author.name}}
</li>
</ul>
As I understand you want this
<p>Authors</p>
<ul>
<li *ngFor='let author of authors'>
<span [hidden]='author.isAvalible'>{{author.name}}</span>
<span (click)='author.isAvalible = !author.isAvalible '>author.isAvalible</span>
</li>
</ul>
<p>Authors</p>
<ul>
<li *ngFor='let author of authors'>
<span [hidden]='author.isAvalible'>{{author.name}}</span>
<span (click)='onClick()'>author.isAvalible</span>
</li>
</ul>
onClick(value){
value=!value;
}

How to access item in <slot> inside v-for (vue.js)

How to send item from v-for to slot? In vue.js.
ListComponent:
<ul>
<li v-for="item in list">
<slot></slot>
</li>
</ul>
Page view:
<list-component :list="list">
<span v-text="item.value"></span>
</list-component>
Code <span v-text="item.value"></span> can't access item (from component's scope). Is it a way to send an item from component to code in tag?
P.S. I know, that as a workaround I can send $index from component to parent view, but it's a little bit hacky
UPD: Example of my current workaround (somebody may find this useful):
Declare index in view's data:
data () {
return {
index: 0,
list: [ ... ]
}
Add to index param to the view's template:
<list-component :list="list" :index="index">
<span v-text="list[index].value"></span>
</list-component>
Declare index in component's props:
props: {
index: {
type: Number
}
}
Add index to v-for's element:
<ul>
<li v-for="item in list" index="$index">
<slot></slot>
</li>
</ul>
the bind expression is compiled in the context who define them,so:
v-text="item.value"
can not be compiled because in Page view there is no "item" defined.
maybe you can try to change your code structure like below:
ListComponent:
//content in component
<span>i am text in component,item value is:{{item.value}}</span>
<slot></slot>
Page view:
<ul>
<li v-for="item in list">
<list-component :item="item"></list-component>
</li>
</ul>

Categories