v-forを指定回数でループさせ、なおかつ配列へアクセスする(Nuxt.js/Vue.js)

今回はNuxt.js(Vue.js)の「v-for」で、指定回数ループさせる方法を考えます。

v-forを指定した回数でループさせる方法

Vue.jsのドキュメントに記載があります。 とても簡単です。

<ul>
<li v-for="n in 10">{{ n }}</li>
</ul>

「10」の部分にループ回数を指定し、「n」に現在のループ番号が入ります。

出力結果はこんな感じです。

<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>

日本語の解説サイト見てると<ul>の方にv-forつけてる例がありましたが、<ul>に付けると<ul>が量産されてしまいます。 私はNuxt.jsで書いていますが、Vue.jsだと違うのか・・・?

指定回数「v-for」の注意点

配列の要素のインデックスが「0」から始まるのに対し、範囲付き「v-for」は「1」から始まります。
このため、「指定回数v-forで回して、配列へアクセスする」場合に注意が必要です。

それならそもそも普通のv-forで配列をループさせたらええんちゃうん。

となるんですが、場合によってはそうもいかない場合があります。

配列の上位数件だけ表示したい場合

配列に5つの要素が入っている↓とします。


export default {
  data() {
    return {
      values: [ 1, 2, 3, 4, 5 ],
    }
  },
}

このうち上位3件だけ表示したいとします。
これを、何も考えずに指定回数v-forで実装するとこんな↓コードになりますが・・・。

<ul>
<li v-for="n in 3">{{ values[n] }}</li>
</ul>

出力結果はこう↓なります。

<ul>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>

「n」が1スタートなせいで、values[1]の「2」から出力されてしまうのです。

なので正しくはこう↓。

<ul>
<li v-for="n in 3">{{ values[n-1] }}</li>
</ul>

ループ番号から1引いてやります。 ちょっと嫌な感じです。

このように、指定回数ループで配列へアクセスする場合はちょっと注意が必要なのですが、この場合はそもそも代替案がけっこうあって、例えば普通のv-forでv-ifを併用する形↓。

<ul>
<li v-for="( value, key ) in values" v-if="key < 3">{{ value }}</li>
</ul>

「values」を2つの引数に展開し(この場合は第2引数に配列のインデックスが入ります)、「key」が3以下なら表示という「v-if」を追加。

出力結果↓。

<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>

他にも、computedで配列の上位3つを切り出す関数を作っておいて、この「top3」でv-forを回すとか。

export default {
  data() {
    return {
      values: [ 1, 2, 3, 4, 5 ],
    }
  },
  computed: {
    top3() {
      return this.values.slice(0, 3)
    },
  },
}

配列の要素数にかかわらず指定回数ループを回したい場合

うまい例が思い浮かばなかったのでゲームの話でいきます。

キャラクターは最大5個の魔法を持てるとします。 キャラごとの魔法の所持数は0~5個になります。

UI上の問題で、この所持数にかかわらず5個の<li>を出力したい、みたいなケースです。

魔法を5個持っていた場合はこんな↓風に出力。

<ul>
<li>メラ</li>
<li>メラミ</li>
<li>メラゾーマ</li>
<li>ベホマ</li>
<li>メホマラー</li>
</ul>

3個の場合はこんな↓風に出力したいとします。

<ul>
<li>メラ</li>
<li>メラミ</li>
<li>メラゾーマ</li>
<li></li>
<li></li>
</ul>

このケースでは、指定回数v-forを使いつつ配列にアクセスのが一番楽かなと思います。

サンプルとして、この↓ような配列があり、

export default {
  data() {
    return {
      values: [ 'メラ', 'メラミ', 'メラゾーマ' ],
    }
  },
}

先ほどのように出力するにはこの↓ようなコード。

<ul>
<li v-for="n in 5">
<span v-if="(n-1) < values.length">{{ values[n-1] }}</span>
</li>
</ul>

v-ifを追加するために、<li>内に<span>を追加しています。
(<li>にv-if追加すると<li>自体が出力されなくなるので)

「n-1」になっているのは「n」が1スタートだからです。

<span>が入るのが嫌な場合はv-html内で条件分岐すればいけました。

<ul>
<li v-for="n in 5" v-html="(n-1) < values.length ? values[n-1] : ''"></li>
</ul>

他のやり方は…こんな↓のとか?

export default {
  data() {
    return {
      values: [ 'メラ', 'メラミ', 'メラゾーマ', '', '' ],
    }
  },
}

配列の方を空の文字列で埋めています。

処理が複雑になってくると面倒なことになりそうな気がしないでもないです。