I am seeking to comprehend how to utilize v-for with v-if's in order to generate repeated teasers without resorting to more simplistic vue-logic. Currently, it appears that when using v-for with v-if nested within, it is not feasible to assign the index number to each teaser for selecting corresponding values.
In the example below, I aim to substitute the "1" with "{{ n }}" in the v-if statement. Is there a method to achieve this?
<script setup lang="ts">
defineProps<{
title: string,
icon1?: string, // possible values: 'sun', 'moon' or 'misc'
textBefore1: string,
textColor1?: string,
textAfter1?: string,
icon2?: string, // possible values: 'sun', 'moon' or 'misc'
textBefore2: string,
textColor2?: string,
textAfter2?: string,
icon3?: string, // possible values: 'sun', 'moon' or 'misc'
textBefore3: string,
textColor3?: string,
textAfter3?: string,
icon4?: string, // possible values: 'sun', 'moon' or 'misc'
textBefore4: string,
textColor4?: string,
textAfter4?: string,
}>()
</script>
<template>
<div class="teaserContent">
<div class="inner">
<div class="positioning--top">
<div class="titleContainer">
<h2 class="title">{{ title }}</h2>
</div>
</div>
<div class="positioningBottom">
<div v-for="n in 4" class="cardElement">
<!-- Repeat the teaser 4 times and replace the number with the index "n" -->
<div class="iconContainer">
<span v-if="icon1 === 'sun' || icon1 === 'moon' || icon1 === 'misc'" class="material-symbols-rounded icon">{{ icon1 === 'sun' ? 'sunny' : icon1 === 'moon' ? 'clear_night' : icon1 === 'misc' ? 'brightness_4' : 'clear_night' }}</span>
<span v-else class="material-symbols-rounded icon">clear_night</span>
</div>
<div class="textContainer">
<span class="text text--before">{{ textBefore1 }}</span>
<span v-if="textColor1" class="text text--color">{{ ' ' + textColor1 }}</span>
<span v-if="textAfter1" class="text text--after">{{ ' ' + textAfter1 }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
// style here ...
</style>
Edit: Added the working Solution
thanks to Keyboard Corporation
CardTeaser.vue:
<script setup lang="ts">
import type {ICardTeaser} from "@/components/CardTeaser/CardTeaser.types";
interface Props {
title: string,
teasers: Array<ICardTeaser>,
}
const props = defineProps<Props>();
const getIcon = (icon: ICardTeaser["icon"]) => {
if (icon === 'sun') {return 'sunny';}
if (icon === 'misc') {return 'brightness_4';}
return 'clear_night';
};
</script>
<template>
<div class="teaserContent">
<div class="inner">
<div class="positioning--top">
<div class="titleContainer">
<h2 class="title">{{ title }}</h2>
</div>
</div>
<div class="positioningBottom">
<div v-for="(teaser, n) in props.teasers" v-bind:key="`cardElement-${n}`" class="cardElement">
<div class="iconContainer">
<span class="material-symbols-rounded icon">{{ getIcon(teaser.icon) }}</span>
</div>
<div class="textContainer">
<span class="text text--before">{{ teaser.textBefore }}</span>
<span v-if="teaser.textColor" class="text text--color">{{ ' ' + teaser.textColor }}</span>
<span v-if="teaser.textAfter" class="text text--after">{{ ' ' + teaser.textAfter }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
// Styles here ...
</style>
CardTeaser.types.ts:
export interface ICardTeaser {
textBefore: string;
textColor?: string;
textAfter?: string;
icon?: 'sun' | 'moon' | 'misc';
}
Implementation of the Template with data:
<CardTeaser title="Card Teaser" :teasers="[
{
textBefore: 'Card 1',
textColor: 'in color',
textAfter: 'with more text',
icon: 'sun',
},
{
textBefore: 'Card 2',
textColor: 'also in Color',
icon: 'moon',
},
{
textBefore: 'Card 3',
textColor: 'in color',
textAfter: 'and more text',
icon: 'misc',
},
{
textBefore: 'Card 4',
textColor: 'with more color',
textAfter: 'and other text',
}
]"/>
(it seems I could also create a smaller Template for each card and simply place those within a CardTeaserContainer or something.)