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.
Great! Thank you very much!
There's a magic workaround https://play.vuejs.org/#eNp9UctOwzAQ/JWVLwUUWtFyqtJKgHqAAyDggIQ5lGRT3Dq25UcoivLvrB1aOFT1yTszOzurbdmVMcMmIJuy3BVWGA8OfTBzrkRttPXQgsUKOqisrmFA0gFXXBVaOQ/CY+1gFhUnbxfZOJtkl++nXOWj3otcqCCRkUuPVAHkpWigOa+0nXEW+0Go3oczmG7w+xeO1Ss9KksdPiSWNCfpz2DM2bxtYYd3XT4iV7LPR/tZLGPeUcpKrIZrpxUt2Mb5nBW6NkKifTBe0BacTSExkVtKqb/uEuZtwGyHF59YbA7ga7eNGGePFh3aBjnbc35pV+h7evF8j1v678lal0GS+gj5hE7LEDP2suugSor9T5fS3qYzCbV6cYutR+V2S8WgUdklPWd0upsjq//FnQwnqY+rjnU/EXWzpQ==
An interesting attempt, but I'm afraid it is not very useful.
In my experiment, you can see:
-
doubledis 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>