Expected v-bind directive on complex <template> / v-for

Viewed 1351

I'm running into a strange issue with a complex loop I'm attempting within my component.

<template v-for="(scorecard, scorecardIndex) in scorecards">
    <template v-for="(property, propertyIndex) in properties">
        <!-- Primary player -->
        <tr v-if="scorecardIndex === 0"
            v-bind:key="propertyIndex"> // THIS LINE
        </tr>
    </template>

    <!-- Secondary players, should only show score -->
    <tr v-if="scorecardIndex > 0"
        v-bind:key="scorecardIndex">    
    </tr>
</template>

Where I'm setting v-bind:key="propertyIndex" I'm getting the following error within VSCode

[vue/valid-v-for] Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive.

The weird thing about this is that I don't actually get an error on my vue compiler, just within VSCode. This makes me think it has something to do directly with VSCode and not necessarily Vue itself.

1 Answers

You're getting this error from eslint which checks your code for possible errors and bad coding style.

When you use v-for on <template>, you need to make sure you define key on each top-level element within the <template>. This is because <template> is not an actual DOM element (within a Vue template) and so each element within it will be repeated at the same level in the DOM which is why key is required on each element at that DOM tree level.

You have a nested <template>, each using v-for, so it gets a bit more complicated. You need to make sure that the key of the first <tr> uses variables defined by both v-fors. Although in your specific case, you are using v-if to limit the element being created to only the first row, so eslint is being too pedantic here. You can silence the warning like this:

<!-- eslint-disable-next-line vue/valid-v-for -->
<tr v-if="scorecardIndex === 0"
    v-bind:key="propertyIndex">
</tr>

But I think you have the order around the wrong way. It makes more sense to first check if it is the first row and then repeat a bunch of <tr>s, instead of the other way around:

<template v-if="scorecardIndex === 0">
    <!-- Primary player -->
    <tr v-for="(property, propertyIndex) in properties"
        v-bind:key="propertyIndex">
    </tr>
</template>

Lastly, you will get some duplicate key warnings because both sets of <tr>s will use keys 0, 1, 2, and so on. Maybe prefix them:

<template v-for="(scorecard, scorecardIndex) in scorecards">
    <template v-if="scorecardIndex === 0">
        <!-- Primary player -->
        <tr v-for="(property, propertyIndex) in properties"
            v-bind:key="'primary-' + propertyIndex">
        </tr>
    </template>

    <!-- Secondary players, should only show score -->
    <tr v-if="scorecardIndex > 0"
        v-bind:key="'secondary-' + scorecardIndex">    
    </tr>
</template>
Related