Using v-if and v-for on the same element is a common Vue mistake that causes two different problems depending on what the
v-if condition references.
When v-if references a variable defined by v-for, the intent is to filter the list to matching items only. In Vue 3,
v-if has higher priority than v-for, so the loop variable is not in scope when v-if is evaluated, which causes
a runtime error. Even in Vue 2, where v-for has higher priority and the variable is accessible, iterating over all items before filtering
them is unnecessary work. Replace the loop variable with a computed property that returns the filtered array.
When v-if is unrelated to the loop variable, the condition controls whether the entire list should be rendered. Placing it on the loop
element re-evaluates the condition once per item instead of once for the whole list. Move the v-if to a parent element so it is evaluated
only once.
<template>
<ul>
<li v-for="user in users" v-if="user.active" :key="user.id"> <!-- Noncompliant: "v-if" cannot access "user", which is defined by "v-for" -->
{{ user.name }}
</li>
</ul>
</template>
<script>
export default {
props: { users: Array },
}
</script>
<template>
<ul>
<li v-for="user in activeUsers" :key="user.id">
{{ user.name }}
</li>
</ul>
</template>
<script>
export default {
props: { users: Array },
computed: {
activeUsers() {
return this.users.filter(user => user.active);
}
}
}
</script>
<template>
<ul>
<li v-for="user in users" v-if="user.active" :key="user.id"> <!-- Noncompliant: "v-if" cannot access "user", which is defined by "v-for" -->
{{ user.name }}
</li>
</ul>
</template>
<script setup lang="ts">
const props = defineProps<{ users: { name: string; active: boolean }[] }>();
</script>
<template>
<ul>
<li v-for="user in activeUsers" :key="user.id">
{{ user.name }}
</li>
</ul>
</template>
<script setup lang="ts">
import { computed } from 'vue';
const props = defineProps<{ users: { name: string; active: boolean }[] }>();
const activeUsers = computed(() => props.users.filter(user => user.active));
</script>
<template>
<ul>
<li v-for="user in users" v-if="showList" :key="user.id"> <!-- Noncompliant: "showList" is re-evaluated for each item in the loop -->
{{ user.name }}
</li>
</ul>
</template>
<template>
<ul v-if="showList">
<li v-for="user in users" :key="user.id">
{{ user.name }}
</li>
</ul>
</template>