ElementUIのNavMenuをv-forで描画


# 概要

ElementUIの[NavMenu](http://element.eleme.io/#/en-US/component/menu)のサイドバー（グループ有り）をv-forで配列を元に生成します。

その際に若干ハマったので備忘録としても記載します。

# ソース

コンポーネントの場合を想定しています。

## template

```html:template
<template>
  <el-menu mode="vertical" default-active="0">
	<el-menu-item-group v-for="(group, index1) in groups" :title="group.title">
	  <el-menu-item v-for="(item, index2) in group.items" :index="getIndex()">
	    {{ item.label }}
	    {{ index }}
	  </el-menu-item>
	</el-menu-item-group>
  </el-menu>
</template>
```

## script

```html:script
<script>
export default {
  data() {
    return {
      index: 0,
      groups: [
        { title: 'TITLE1',
          items: [
            { label: 'ITEM1' },
            { label: 'ITEM2' },
          ]
        },
        { title: 'TITLE2',
          items: [
            { label: 'ITEM3' },
            { label: 'ITEM4' },
            { label: 'ITEM5' },
          ]
        },
        { title: 'TITLE3',
          items: [
            { label: 'ITEM6' },
            { label: 'ITEM7' },
            { label: 'ITEM8' },
          ]
        }
      ]
    }
  },
  methods: {
    getIndex() {
      return String(this.index++)
    }
  }
}
</script>
```

# 画面イメージ
![Kobito.xYzidJ.png](https://qiita-image-store.s3.amazonaws.com/0/172884/e4254296-fe04-fe55-e98b-a806e40e5b6b.png "Kobito.xYzidJ.png")


# 解説

`<el-menu-item>`のindexは、選択状態を制御するため、`<el-menu>`内では一般的に一意になる必要があります。
全体のアイテム数を取得するため、`getIndex()`という関数の中で、`index`プロパティをインクリメントして返すようにしました。

# レンダリング時にエラー

しかし、上記の方法だとコンソールにエラーログが吐かれてしまいます。

![Kobito.MoagBY.png](https://qiita-image-store.s3.amazonaws.com/0/172884/d4d4fb88-e568-b495-9171-bb50ebf3768c.png "Kobito.MoagBY.png")

*[Vue warn]: You may have an infinite update loop in a component render function.*

と出ていますが、なんのことかさっぱりわかりません。

# 解決策

どうやらインクリメントが悪さをしていたようです。
というか、インクリメントではなく、`this.itemIndex += 1`でもエラーは出ます。
原因はわかりませんが、`v-for`の処理の中で、`data`プロパティの値を変更するのは**NG**のようです。。
※ 根本の原因はわかりませんでした。誰かお分かりでしたら教えて頂けると幸いです！

## 修正コード

### template

```html:template
<template>
  <el-menu mode="vertical" default-active="0">
    <el-menu-item-group v-for="(group, index1) in groups"
        :key="group.title" :title="group.title">
      <el-menu-item v-for="(item, index2) in group.items"
          :key="item.label" :index="getIndex(groups, index1, index2)">
        {{ item.label }}
        {{ getIndex(groups, index1, index2) }}
      </el-menu-item>
    </el-menu-item-group>
  </el-menu>
</template>
```

### script

```html:script
<script>
export default {
  data() {
    return {
      // index: 0, <-不要
      groups: [
        { title: 'TITLE1',
          items: [
            { label: 'ITEM1' },
            { label: 'ITEM2' },
          ]
        },
        { title: 'TITLE2',
          items: [
            { label: 'ITEM3' },
            { label: 'ITEM4' },
            { label: 'ITEM5' },
          ]
        },
        { title: 'TITLE3',
          items: [
            { label: 'ITEM6' },
            { label: 'ITEM7' },
            { label: 'ITEM8' },
          ]
        }
      ]
    }
  },
  methods: {
    getIndex(groups, groupIdx, itemIdx) {
      let total = 0
      for (var i = groupIdx - 1; i >= 0; i--) {
        total += groups[i].items.length
      }
      total += itemIdx
      return String(total)
    }
  }
}
</script>

```

- `data.index`は使用できなかったので、不要のため削除します。
- `methods.getIndex`に、**v-forとネストv-for**の引数を3つ設定し、それらでアイテムの通し番号を計算します。

あんまりテストしていませんが、これで通し番号も取得でき、エラーも回避できたはずです。

ちなみに、String型で返却している理由は、`<el-menu-item>`の`index`プロパティがString型しか許容していないためです。

以上。