core icon indicating copy to clipboard operation
core copied to clipboard

Variable assignment in loops in template

Open grandsong opened this issue 3 years ago • 3 comments

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")

grandsong avatar Nov 24 '22 07:11 grandsong

It's easy to solve. see demo

edison1105 avatar Nov 24 '22 10:11 edison1105

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.

grandsong avatar Nov 24 '22 10:11 grandsong

Great! Thank you very much!

grandsong avatar Nov 28 '22 01:11 grandsong

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==

edison1105 avatar Sep 06 '23 08:09 edison1105

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.

grandsong avatar Sep 07 '23 02:09 grandsong