LoginSignup
9
7

More than 3 years have passed since last update.

Vueitfyのv-list-itemでネストしたpathの子のページに行くと親もactiveになってしまう問題

Last updated at Posted at 2020-07-13

あらすじ

  1. /items/items/editのようなリンクのあるサイドバーを実現したい
  2. デフォルトでは/items/editを開くと/itemsもアクティブになるので困る
  3. vue-router:exactのpropをBooleanで渡して完全一致か否かを指定する
  4. exactでは/items?category=プロテインの様にQuery Paramを指定するとアクティブにならない
  5. 特定ページにいる場合、:topropに渡すpathにもquery paramを渡してあげる
  6. 解決!!

解決コード

<template>
  <v-app>
    <v-navigation-drawer v-model="drawer" app>
      <v-list dense nav>
        <template v-for="(page, index) in pageList">
          <!-- ネストしていない構造 -->
          <v-list-item
            v-if="typeof page.items !== 'object'"
            :key="index"
            :to="page.to"
            :exact="page.exact"
          >
            <v-list-item-action>
              <v-icon>{{ page.icon }}</v-icon>
            </v-list-item-action>
            <v-list-item-content>
              <v-list-item-title v-text="page.title" />
            </v-list-item-content>
          </v-list-item>

          <!-- ネストしている構造 -->
          <v-list-group
            v-else
            :key="index"
            :prepend-icon="page.icon"
          >
            <template v-slot:activator>
              <v-list-item-content>
                <v-list-item-title v-text="page.title" />
              </v-list-item-content>
            </template>
            <v-list-item
              v-for="(childPage, cIndex) in page.items"
              :key="cIndex"
              :to="childPage.to"
              :exact="childPage.exact"
            >
              <v-list-item-icon>
                <v-icon>{{ childPage.icon }}</v-icon>
              </v-list-item-icon>
              <v-list-item-content>
                <v-list-item-title v-text="childPage.title" />
              </v-list-item-content>
            </v-list-item>
          </v-list-group>
        </template>
      </v-list>
    </v-navigation-drawer>
  </v-app>
</template>

<script>
export default {
  computed: {
    pageList () {
      const route = this.$route
      // NOTE: どうしてもクエリーパラメーターを使いたい場合は当該pathにいる場合にrouteからfullPathを取得して当てる
      const itemsPath = route.path === '/items' ? route.fullPath : '/items'
      return [
         // ホームはexactをtrueにしなくても問題無い
        {
           icon: 'home',
           title: 'ホーム',
           to: '/',
           exact: true,
        },
        {
           icon: 'shopping_cart',
           title: '商品',
           // `/items/`以下に反応されたら困るのでexactをfalse
           // NOTE: しかし、このままだとクエリーパラメーターを使えない
           items: [
             {
               title: '商品一覧',
               // to: '/items'
               to: itemsPath,
               exact: true,
             },
             // 新規と編集の両方を許可したいのでexacrtはfalse
             {
               title: '商品登録',
               to: '/items/edit',
               exact: false,
             },
           ]
        },
      ]
    }
  }
}
</script>

解説

デフォルトでは/items/editを開くと/itemsもアクティブになるので困る

vue-touterはデフォルトでは部分一致でアクティブの判定をしてしまいます。
/items/items/editのような構造のpathがある場合はexactで完全一致でアクティブ判定をするように指定してあげます。

この時、exactはBooleanで指定できるので
完全一致にしたいものだけexactをtrueにしていしてあげる事もできます。

  <v-list-item
     v-if="typeof page.items !== 'object'"
     :key="index"
     :to="page.to"
     :exact="page.exact"
  >

exact(完全一致)ではQuery Stringを指定するとアクティブにならない

exactでは/items?category=プロテインのようにQuery Stringを指定するとアクティブにならない

GitHubのissuesなどで色々漁った結果、公式で対応する予定は無さそうな雰囲気がしたので無理やり対応してみます。

特定ページにいる場合、クエリーも渡す

参考
How do I style an active route that has query params? - by stackoverflow

ちょっと強引な解決方法になりますが、もし他にいい方法が有ればコメントで教えて下さい!!

<script>
export default {
  computed: {
    pageList () {
      const route = this.$route
      // NOTE: どうしてもクエリーパラメーターを使いたい場合は当該pathにいる場合にrouteからfullPathを取得して当てる
      const itemsPath = route.path === '/items' ? route.fullPath : '/items'

      return [{
         icon: 'shopping_cart',
         title: '商品一覧',
         to: itemsPath,
         exact: true,
      }]
    }
  }
}
</script>
9
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7