Vue how to add element to v-for loop - javascript

In my vue-app I want to add an element inside a v-for-loop. So I tried to do this:
<li v-for="(slide, index) in slides" :key="index"
:class="slideIndex === index ? 'active' : ''"
#click="slideIndex = index"
>
{{ slide.nodeName }}
</li>
<li class="cursor-pointer item" v-if="slides.length === 5">foobar</li>
but this doesn't keep the "new" item inside the index e.g. I want the element to be inside the loop.
How can I solve that?

Do you mean that if slides.length === 5, then add your second <li> tag after each <li> tag?
If yes, please take a look at the following code
<template v-for="(slide, index) in slides">
<li
:key="slide.nodeName"
:class="slideIndex === index ? 'active' : ''"
#click="slideIndex = index"
>
{{ slide.nodeName }}
</li>
<li
:key="`${slide.nodeName}_${index}`"
class="cursor-pointer item"
v-if="slides.length === 5"
>
foobar
</li>
</template>
Please pay attention to the key value binding of the <li> tag at the same level

<el-row v-for="item in queryFolderList" :key="item.id">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-button-group>
<el-button type="primary" size="mini" #click="goToDetil(item.id)"
>{{ item.id }}</el-button
>
<el-button type="warning" size="mini" #click="submit(item.id)"
>{{ item.name}}</el-button
>
</el-button-group>
</el-col>

To include elements to the loop of other elements in vue.js, you need to include them into their tag. In your case you need to be aware of, that nesting <li> into <li> can cause problems. Try this:
<li v-for="(slide, index) in slides" :key="index"
:class="slideIndex === index ? 'active' : ''"
#click="slideIndex = index"
>
{{ slide.nodeName }}
<ul v-if="slides.length === 5">
<li class="cursor-pointer item">foobar</li>
</ul>
</li>
As the loop is on your first <li> element, everything between <li> and </li> of that element will be included into the loop. I added an <ul> around your second <li> for the nested list.
EDIT: Render second <li> if 5 slides in slides
So now that I think to know what you trying to achieve, let´s try to solve it. The second <li> has to be rendered after 5 loops done/if there are 5 slides in slides.
First you need to note, that v-if="slides.length === 5" only matches, if there are exactly 5 slides. So if you now provide something with a slides.length bigger than 5 or smaller, your <li> with foobar don´t show. Change it to v-if="slides.length > 5" instead. If <li> with foobar only should be shown if the length IS 5, then your code is working as it should.
But if you try to add <li> with foobar AFTER 5 items or even every 5 items, you could use this little trick:
<ul>
<span v-for="(slide, index) in slides" :key="index">
<li :class="slideIndex === index ? 'active' : ''"
#click="slideIndex = index"
>
{{ slide.nodeName }}
</li>
<li class="cursor-pointer item" v-if="(index + 1)%5 === 0">foobar</li>
</span>
</ul>
You shouldn´t include <span> in <ul>, but it should work. Now the loop runs for both <li> elements, but <li> with foobar only renders after each 5th element, due to v-if="(index + 1)%5 === 0".
Hope this helped you.

Related

How to use <parent> and <child> component in template recursion in angular

I have 2 components <parent> and <child>. I want to use them in recursive <ng-template>
let say for the sake of simplicity
<parent> component will contain <ul>
<child> component will contain <li>
here is what I have referenced https://gist.github.com/arniebradfo/5cf89c362cc216df6fc1d9ca4d536b72
here is how I have tried
<div>
<ng-template #recursiveList let-list>
<span *ngFor="let item of list">
<!-- commented {{item.title}} -->
<child [item]="item"/>
<!-- commented <ul *ngIf="item.children.length > 0"> -->
<template>
<parent [item]="item"/>
<ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: item.children }"></ng-container>
</template>
<!-- commented </ul> -->
</span>
</ng-template>
<ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: list }"></ng-container>
</div>
but above code does not work properly
my expected result should look like below shown, for a parent with 2 childs
<span>
<parent>
<child/> // <-- child 1
<child/> // <-- child 2
</span>
with html it look like below
<span>
<ul>Parent</ul>
<li>child 1</li> // <-- child 1
<li>child 1</li> // <-- child 2
</span>
it must follow same nesting with any level.
please help me with a demo from https://gist.github.com/arniebradfo/5cf89c362cc216df6fc1d9ca4d536b72
Please help me thanks in advance !!!
main.component.html
<div>
<parent *ngFor="let item of list" [item]="item">
</div>
parent.component.html
<span>{{item.name}}</span>
<ul>
<child *ngFor="let child of item.children" [item]="child">
</ul>
child.component.html
<li>
<parent [item]="item">
</li>

Submenu is not working for multiple items in ReactJs?

I have submenu that is working for only when there is single item single LI but if I increase LI then its not working fine. How can I solve this?
My Code:-
import React, { useState } from "react";
import { Link } from "react-router-dom";
const Home = (props) => {
const [subMenuOpen, setSubMenuOpen] = useState(false);
return (
<>
<ul className="submenu">
<li>
<Link to="/">
<div
onClick={() => setSubMenuOpen(!subMenuOpen)}
>
<span>test</span>
</div>
</Link>
<ul class={`sub-menu ${subMenuOpen ? "is-open" : ""}`}>
<li className="menu-item">Sub-Item 1</li>
<li className="menu-item">Sub-Item 2</li>
<li className="menu-item">Sub-Item 3</li>
</ul>
</li>
<li>
<Link to="/">
<div
onClick={() => setSubMenuOpen(!subMenuOpen)}
>
<span>test</span>
</div>
</Link>
<ul class={`sub-menu ${subMenuOpen ? "is-open" : ""}`}>
<li className="menu-item">Sub-Item 1</li>
<li className="menu-item">Sub-Item 2</li>
<li className="menu-item">Sub-Item 3</li>
</ul>
</li>
</ul>
</>
);
};
export default Home;
.sub-menu { display: none;}
.sub-menu.is-open { display:block;}
ThankYou!
Because you have multiple submenus, you need to keep track of which one is open, and you can't do that with a single bool value.
If you will have only 1 level of child menu items, and at most one of them can be open, you can use a number: -1 -> no subitem is open, 0 -> the first item is open, etc. like this:
const [subMenuOpen, setSubMenuOpen] = useState(-1);
1st div: <div onClick={() => setSubMenuOpen(0)}>
2nd div: <div onClick={() => setSubMenuOpen(1)}>
(etc)
1st ul: <ul class={`sub-menu ${subMenuOpen === 0 ? "is-open" : ""}`}>
2nd ul: <ul class={`sub-menu ${subMenuOpen === 1 ? "is-open" : ""}`}>
(etc)
you probably also need to provision something that clears the menu, so clicking outside of the menu would run setSubMenuOpen(-1).
To get toggling behaviour, you can use a helper function like this:
helper function: const toggleMenu = (x) => setSubMenuOpen(subMenuOpen === x ? -1 : x)
1st div: <div onClick={() => toggleMenu(0)}>
2nd div: <div onClick={() => toggleMenu(1)}>
If you plan to have multiple levels of menu items, then a tree-like data structure will be more suitable, perhaps to both define the menu and to keep track of the open (sub)(sub)(sub)item.
try using "className" instead of "class"
JSX seems to be right
I think you have a problem in css

Vuejs & Vuetify : Is there a way to loop around template with datatable from vuetify (with v-slot dynamic)?

I have several datatable with server-side pagination, search fields and filterable fields
However, I would like to be able to generate a component and use the props to avoid modifying the same thing three times
Is there a way to loop around template with datatable from vuetify (with v-slot dynamic) ?
for example :
<template v-slot:header.id="{ header }">
<div class="pointer border-right pa-2">
<span class="title" #click.prevent="sortBy('id')">
ID
<v-icon class="ml-2">{{icon.id}}</v-icon>
</span>
<v-text-field v-model="searcher.ticket_id.value" type="text"
label="Rechercher..."></v-text-field>
</div>
</template>
<template v-slot:header.name="{ header }">
<div class="pointer border-right pa-2">
<span class="title" #click.prevent="sortBy('name')">
Nom du ticket
<v-icon class="ml-2">{{icon.name}}</v-icon>
</span>
<v-text-field v-model="searcher.ticket_name.value" type="text"
label="Rechercher..."></v-text-field>
</div>
</template>
Become (not functional) :
<template v-for="(item, i) in headers" v-slot:item.text="{ header }">
<div class="pointer border-right pa-2">
<span class="title" #click.prevent="sortBy(item.name)">
{{item.name}}
<v-icon class="ml-2">{{icon[item.name]}}</v-icon>
</span>
<v-text-field v-model="item.searcher" type="text"
label="Rechercher..."></v-text-field>
</div>
</template>
If i add the key , i have '<template>' cannot be keyed. Place the key on real elements instead
and if i remove this i have Elements in iteration expect to have 'v-bind:key' directives
However I came across several sources that demonstrate the opposite
https://v2.vuejs.org/v2/guide/list.html#v-for-on-a-lt-template-gt
Fix: require-v-for-key shouldn't be raised on slots
vue-language-server : Elements in iteration expect to have 'v-bind:key' directives
Thank you
Edit 20/12 :
https://github.com/vuejs/eslint-plugin-vue/issues/1006
How can I make the v-slot attribute dynamically with the vuetify data table ?
Here is my code :
<template v-for="(head, i) in headers" v-slot:header[header.value]="{header}">
<div :key="i" class="pointer border-right pa-2">
<span class="title" #click.prevent="sortBy('id')">
{{head}} <br> {{header}}
<v-icon class="ml-2">{{icon.id}}</v-icon>
</span>
<v-text-field v-model="searcher.ticket_id.value" type="text"
label="Rechercher..."></v-text-field>
</div>
</template>
It cannot be mounted on the table column
I need to use v-slot:header.id="{header}" for the mounted on the id column but I have to make id dynamic according to my loop
how to do ?
Thank you
I find it
I added head: 'header.id' in my list so i can use <template v-for="(head, i) in headers" v-slot:[headers[i].head]="{header}">
I had to do
<template v-for="(col, i) in filters" v-slot:[`header.${i}`]="{ header }">
in my case
filters: { 'name': [], 'calories': [] },

React variable evaluation when 'push' JSX elements in an array

I'm pushing JSX elements in an array
for (category of this.state.categories) {
categories.push(
<li>
<label
className="tree-toggler nav-header list-group-item"
onClick={() => this.showHideConnectorNames(category)}
>
{category}
</label>
<ul
className="tree ul-no-style"
id={category+'Connectors'}
>
</ul>
</li>
);
}
Problem is that category variable in this.showHideConnectorNames(category) method call evaluates to last category in this.state.categories array (so 'cat2' for all elements). It is evaluated correctly at all the other places, such as inside the <label>.
So I have to do this:
for (category of this.state.categories) {
if (category === 'cat1')
categories.push(
<li>
<label
className="tree-toggler nav-header list-group-item"
onClick={() => this.showHideConnectorNames('cat1')}
>
{category}
</label>
<ul
className="tree ul-no-style"
id={category + 'Connectors'}
>
</ul>
</li>
);
else if (category === 'cat2')
categories.push(
<li>
<label
className="tree-toggler nav-header list-group-item"
onClick={() => this.showHideConnectorNames('cat2')}
>
{category}
</label>
<ul
className="tree ul-no-style"
id={category + 'Connectors'}
>
</ul>
</li>
);
else
categories.push(
<li>
<label
className="tree-toggler nav-header list-group-item"
onClick={() => this.showHideConnectorNames(category)}
>
{category}
</label>
<ul
className="tree ul-no-style"
id={category + 'Connectors'}
>
</ul>
</li>
);
}
Is this a React issue or am I doing something wrong?
Not sure what you want to do with the array after it's built, but I think using a map call would be quite a bit more efficient. Just place this inside your return statement wherever you want to render your list items:
<ul>
{this.state.categories.map(cat => (
// your li has a missing key property
<li>
<label
className="tree-toggler nav-header list-group-item"
onClick={() => this.showHideConnectorNames(cat)}
>
{cat}
</label>
<ul
className="tree ul-no-style"
id={cat + 'Connectors'}
></ul>
</li>
))}
</ul>
You are using for of incorrectly to prevent using it incorrectly I like to use map instead:
const categories = this.state.categories.map(category => (
// not sure what to use for key here but it's missing
<li key={category.id}>
<label
className="tree-toggler nav-header list-group-item"
onClick={() => this.showHideConnectorNames(category)}
>
{category}
</label>
<ul
className="tree ul-no-style"
id={category + 'Connectors'}
></ul>
</li>
));
This is because category is set in every loop iteration so after the loop is finished category is the last one. That is why by the time you click on your component the category is always the last one from this.state categories.
Here is a demo showing the bahavior:
let category,categories=[],cats=[1,2,3];
//you re assing category every time
for (category of cats) {
categories.push(
()=>console.log('category is now:',category)
);
}
//now category is the last one
console.log('category after loop:',category);
//now I execute the functions category is the last one
categories.forEach(cat=>cat())
just add let, then category will be local inside for scope
for (let category of this.state.categories)

Insert item after every nth item in v-for loop

I am iterating through a list of products and displaying them in cards. I'd like to display a promotion after every 18th product listed:
<div
v-for="(prod, index) in products"
:key="index"
class="w-full md:w-1/3 lg:w-1/4 xl:w-1/4 mb-8 px-2
>
<promotion v-if="(index % 18 == 0) && index != 0" ></promotion>
<product-card v-else :product="prod"></product-card>
</div>
The way I have this written now, the promotion is displayed but it is inserted in place of the item with the matching index. How do I place the promotion before or after the nth item without replacing it?
To preserve grid css, you can put them under a template and use v-for
<template v-for="(prod, index) in products">
<div class="w-full md:w-1/3 lg:w-1/4 xl:w-1/4 mb-8 px-2"
v-if="(index % 18 == 0) && index != 0"
:key="'promotion-${index}'">
<promotion></promotion>
</div>
<div class="w-full md:w-1/3 lg:w-1/4 xl:w-1/4 mb-8 px-2"
:key="'product-${index}'">
<product-card :product="prod"></product-card>
</div>
</template>
I think you just need to remove the v-else here inside the product-card element
<product-card :product="prod"></product-card>

Categories

Resources