Search code examples
vue.jsvuejs2v-for

Vue `v-for` directive with integer range bound to component property?


I am experimenting with Vue.js (version 2.5.16) and its v-for directive which should be able to repeat an element according to some integer range, according to the official Vue.js documentation. Concretely, I am trying to write a component that draws a number of circular counters based on an integer-valued property.

The following snippet, containing the hard-coded literal value 10, does indeed render precisely ten circles: (jsfiddle)

    <svg class="counter" v-for="n in 10" :key="n"
         xmlns="http://www.w3.org/2000/svg" version="1.1"
         xmlns:xlink="http://www.w3.org/1999/xlink"
         viewBox="0 0 10 10">
        <circle cx="5" cy="5" r="5"></circle>
    </svg>

However, hard-coding the value is of limited utility. I have added an integer-valued property to my component as follows: (typescript)

export default Vue.extend({
    props: {
        counter: Number
    },
    ...

... and tried the following variants of the v-for directive:

  • v-for="n in counter" :key="n" (jsfiddle)
  • v-for="n in {counter}" :key="n" (jsfiddle)

But neither of them achieve a variable number of rendered circles. (Note: I have employed the Vue developer tools to ensure that the counter property of the component does, in fact, hold the correct value.)

This brings me to my question: How do you use v-for with an integer range set by a property of your component?

If this is not possible, then the integer-range support of v-for is indeed rather useless. How often does one want to use a hard-coded range?

However, I still want the behaviour. How would one implement it without v-for? I can think of several possible alternatives:

  1. Write my own render function.
  2. Use the counter property in a computed property that returns an array of the desired size and use v-for on that array.
  3. Bind v-for to an array and hook into changes of the counter property to update that internal array using only array mutations that are listed on the array change detection page so that Vue does not discard and rebuild the entire DOM substructure on every change.

Option 1 seems like a tonne of work for such a simple use-case. Option 2 is trivially easy but I fear that it will cause Vue to discard and regenerate all the repeated child elements on every change. Option 3 seems like it would perform the best, if it is possible, but I don't really know how to go about it. (As I said, I am investigating Vue for the first time.)

What to do?


Solution

  • You just have to bind value to your counter property.Let's assume your component is called circ.

    <div id="app">
      <circ :counter="10"></circ>
    </div>
    
    <template id="circ">
      <div>
        <svg class="counter" v-for="n in counter" :key="n"
             xmlns="http://www.w3.org/2000/svg" version="1.1"
             xmlns:xlink="http://www.w3.org/1999/xlink"
             viewBox="0 0 10 10">
             <circle cx="5" cy="5" r="5"></circle>
        </svg>
      </div>
    </template>
    

    Demo: http://jsbin.com/vebijiyini/edit?html,js,output