Vue: Как переключить класс 'open' для каждой записи в динамически генерируемом меню - PullRequest
0 голосов
/ 04 апреля 2019

У меня есть Меню, которое динамически генерируется из данного объекта, который содержит пункты меню. Меню имеет три уровня. Чтобы открыть подменю (и подменю), я должен соответственно переключить класс open для элемента li. Я изо всех сил пытаюсь выяснить логику этого.

Объект меню:

menu: [
    {
      id: '1',
      category: 'Home',
      items: [
        {
          id: '1.1',
          name: 'Dashboard',
          icon: 'home',
          target: 'home'
        },
        {
          id: '1.2',
          name: 'About',
          icon: 'info-circle',
          target: 'about'
        }
      ]
    },
    {
      id: '2',
      category: 'Help',
      items: [
        {
          id: '2.1',
          name: '1st Lvl Entry 1',
          icon: 'home',
          items: [
            {
              id: '2.1.1',
              name: '2nd Lvl Entry 1',
              icon: 'info-circle',
              target: 'about'
            },
            {
              id: '2.1.2',
              name: '2nd Lvl Entry 2',
              icon: 'home',
              items: [
                {
                  id: '2.1.2.1',
                  name: '3nd Lvl Entry 1',
                  icon: 'home',
                  target: 'home'
                },
                {
                  id: '2.1.2.2',
                  name: '3nd Lvl Entry 2',
                  icon: 'info-circle',
                  target: 'about'
                }
              ]
            },
            {
              id: '2.1.3',
              name: '2nd Lvl Entry 3',
              icon: 'home',
              items: [
                {
                  id: '2.1.3.1',
                  name: '3nd Lvl Entry 3',
                  icon: 'info-circle',
                  target: 'about'
                },
                {
                  id: '2.1.3.2',
                  name: '3nd Lvl Entry 4',
                  icon: 'home',
                  target: 'home'
                }
              ]
            }
          ]
        },
        {
          id: '2.2',
          name: '1st Lvl Entry 2',
          icon: 'home',
          target: 'home'
        }
      ]
    }
  ]

Компонент меню:

<template>
  <div class="bg-white">
    <div class="content">
      <!-- Toggle Main Navigation (mobile only) -->
      <div class="d-lg-none push">
        <button
          type="button"
          @click="hideMobileNav = !hideMobileNav"
          class="btn btn-block btn-light d-flex justify-content-between align-items-center"
        >
          Menu
          <font-awesome-icon icon="bars" />
        </button>
      </div>
      <!-- END Toggle Main Navigation -->

      <!-- Main Navigation -->
      <div
        id="main-navigation"
        class="d-lg-block push"
        :class="{ 'd-none': hideMobileNav }"
      >
        <first-level :items="menu" />
      </div>
      <!-- END Main Navigation -->
    </div>
  </div>
</template>

<script>
import firstLevel from './firstLevel'

export default {
  name: 'mainNav',
  components: {
    firstLevel
  },
  data() {
    return {
      hideMobileNav: true
      menu: [] // see menu snippet above
    }
  }
}
</script>

Компонент первого уровня:

<template>
  <ul class="nav-main nav-main-horizontal nav-main-hover">
    <template v-for="firstLevel in items">
      <li
        :key="firstLevel.category"
        v-text="firstLevel.category"
        class="nav-main-heading"
      ></li>
      <li
        v-for="item in firstLevel.items"
        :key="item.id"
        class="nav-main-item"
        :class="{ open: item.isOpen }"
      >
        <router-link
          :to="{ name: item.target }"
          class="nav-main-link"
          :class="{ 'nav-main-link-submenu': item.items }"
        >
          <font-awesome-icon
            :icon="item.icon"
            fixed-width
            class="nav-main-link-icon"
          />
          <span v-text="item.name" class="nav-main-link-name"></span>
        </router-link>
        <template v-if="item.items">
          <second-level :items="item.items" />
        </template>
      </li>
    </template>
  </ul>
</template>

<script>
import secondLevel from './secondLevel'
export default {
  name: 'firstLevel',
  props: ['items'],
  components: {
    secondLevel
  }
}
</script>

Компонент второго уровня:

<template>
  <ul class="nav-main-submenu">
    <li
      v-for="item in items"
      :key="item.id"
      class="nav-main-item"
    >
      <router-link
        :to="{ name: item.target }"
        class="nav-main-link"
        :class="{ 'nav-main-link-submenu': item.items }"
      >
        <font-awesome-icon
          :icon="item.icon"
          fixed-width
          class="nav-main-link-icon"
        />
        <span v-text="item.name" class="nav-main-link-name"></span>
      </router-link>
      <template v-if="item.items">
        <third-level :items="item.items" />
      </template>
    </li>
  </ul>
</template>

<script>
import thirdLevel from './thirdLevel'

export default {
  name: 'secondLevel',
  props: ['items'],
  components: {
    thirdLevel
  }
}
</script>

Компонент третьего уровня:

<template>
  <ul class="nav-main-submenu">
    <li
      v-for="item in items"
      :key="item.id"
      class="nav-main-item"
    >
      <router-link :to="{ name: item.target }" class="nav-main-link">
        <font-awesome-icon
          :icon="item.icon"
          fixed-width
          class="nav-main-link-icon"
        />
        <span v-text="item.name" class="nav-main-link-name"></span>
      </router-link>
    </li>
  </ul>
</template>

<script>

export default {
  name: 'thirdLevel',
  props: ['items']
}
</script>

Вот скриншот, чтобы получить лучшую идею: menu screenshot

Ожидаемое поведение должно состоять в том, что при нажатии на пункт меню вы попадете к цели ИЛИ откроете подменю, если оно существует, добавив / удалив класс open к элементу li (класс: nav-main-item). При нажатии на другой пункт меню текущее подменю должно закрыться и открыть новое меню.

Спасибо за любую помощь. Очень ценю это.

...