Issue
I want to understand how to use v-for with v-if's to write repetitive teasers without more simple vue-logic. Now it seems, when using v-for with v-if within, it is not possible to write the index number to each teaser to select the corresponding values.
I want to replace the "1" in the below example with "{{ n }}" in the v-if statement. How is that possible?
<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',
}
]"/>
(seems I could also do a smaller Template for each card and just put those within a CardTeaserContainer or somehting.)
Solution
You cannot directly use the iterator variable n from v-for inside v-if as a string, but you can do this by creating a computed
property that returns the correct prop based on the index.
First, define your props as an array of objects:
defineProps({
teasers: {
type: Array,
default: () => []
},
})
Each object in the teasers
should have the properties icon
, textBefore
, textColor
and textAfter
.
Then, in your template, you use v-for
to iterate over the teasers
:
<div v-for="(teaser, n) in teasers" class="cardElement">
<div class="iconContainer">
<span v-if="teaser.icon === 'sun' || teaser.icon === 'moon' || teaser.icon === 'misc'" class="material-symbols-rounded icon">{{ teaser.icon === 'sun' ? 'sunny' : teaser.icon === 'moon' ? 'clear_night' : teaser.icon === '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">{{ 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>
Answered By - Keyboard Corporation
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.