Subscribe on changes!

Variable assignment in loops in template

avatar
Nov 24th 2022

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")
avatar
Nov 24th 2022

It's easy to solve. see demo

avatar
Nov 24th 2022

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.

avatar
Nov 28th 2022

Great! Thank you very much!

avatar
Sep 7th 2023

An interesting attempt, but I'm afraid it is not very useful.

In my experiment, you can see:

  1. doubled is undefined before the <v-for> loop but remains after it.
  2. Each element <p>from the loop has its own item, but they share the identical doubled.

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>

You can run the playground here.