Variable assignment in loops in template
What problem does this feature solve?
Sometimes, we need to compute a variable for each iteration of a loop and use this varible for multiple times. An example:
.SimpleGrid
.row(v-for="(v, row_idx) in row_count_")
.cell(v-for="(v, column_idx) in column_count")
slot(v-if="items[row_idx * column_count + column_idx]" :item="items[row_idx * column_count + column_idx]")
Here, items[row_idx * column_count + column_idx]
is repeated.
Note: the total cells may be more than the length of items
, so v-if="items[...]"
is needed.
So far, I've discovered only one hack, thanks to How to define variable in vue template?:
.SimpleGrid
.row(v-for="(v, row_idx) in row_count_")
.cell(v-for="(v, column_idx) in column_count")
template(v-for="item in [ items[row_idx * column_count + column_idx] ]")
slot(v-if="item" :item="item")
It works. But we still need a formal way instead of such hack.
What does the proposed API look like?
In most (if not all) cases, variables follow loop iterations. (Otherwise, we should use computed
for the whole component.)
I suggest a new directive v-let
besides v-for
:
.SimpleGrid
.row(v-for="(v, row_idx) in row_count_")
.cell(v-for="(v, column_idx) in column_count" v-let="item = items[row_idx * column_count + column_idx]")
slot(v-if="item" :item="item")
It's easy to solve. see demo
Thanks all the same. But you don't seem to understand what I really need. I don't need a sub-component, which unnecessarily complicates code while I want to make code less and neat.
An interesting attempt, but I'm afraid it is not very useful.
In my experiment, you can see:
doubled
is undefined before the<v-for>
loop but remains after it.- Each element
<p>
from the loop has its ownitem
, but they share the identicaldoubled
.
Click the first button "2" and you'll see an alert with the message "1 * 2 = 6" where usually we would want "1 * 2 = 2".
<script setup>
import { ref } from 'vue'
const items = ref([1,3])
function show(item, doubled) {
alert(item + ' * 2 = ' + doubled )
}
</script>
<template>
<p>this.doubled: {{doubled}}</p>
<p v-for="item in items" :key="item" :XXXX="console.log('[loop]', doubled = item * 2, this)">
<button @click="show(item, doubled)">{{ doubled }}</button>
</p>
<p>this.doubled: {{doubled}}</p>
</template>
You can run the playground here.
Moreover, when I use the old way of SFC, and do a little trick, I found that doubled
is actually assigned to the vue instance.
For console.warn('[loop], 'doubled = item * 2, this)
, this
is the vue instance.
Or you can click the button "show_doubled".
<script>
export default {
data(){
return {
items: [1, 3]
}
},
methods: {
show(msg) {
alert(msg)
},
show_doubled() {
console.log('this', this)
alert(this.doubled)
},
},
}
</script>
<template>
<p
v-for="item in items" :key="item"
:XXXX="console.warn('[loop]', doubled = item * 2, this)"
>
<button @click="show(doubled)">doubled number: {{ doubled }}</button>
</p>
<button @click="show_doubled">check_doubled</button>
</template>