How to set active state on a menu tree with 2 child arrays vue 3?

问题描述 投票:0回答:1

我有一个带有 NavBarItem 组件的侧边栏,它接收一个 navItem 对象作为道具,我想在单击链接时将活动状态更改为 true。

导航栏.vue

<template>
      <nav class="mt-2 px-2">
        <NavBarItem
          @click="setActiveLink(item, _index)"
          :item="item"
          v-for="(item, _index) in navItems"
          :key="item.label"
        />
      </nav>
</template>

<script setup>
  const setActiveLink = (el, i) => {
    // 🤬🤯
  }

  const navItems = [
    {
      href: '#',
      active: false,
      label: 'VehicleBuild',
      icon: vehicleTruckCube,
      children: [
        {
          href: '/vehicle-packages',
          active: false,
          label: 'Paket',
          icon: CubeIcon,
          children: [],
        },
      ],
    }]
</script>

NavBarItem.vue

<script setup>
  import { RouterLink } from 'vue-router'
  import { computed } from 'vue'
  import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'
  import { ChevronDownIcon } from '@heroicons/vue/24/outline'
  const props = defineProps({
    item: Object,
  })

  const hasActiveChild = computed(() => {
    function hasActiveItem(items) {
      return items.some(item => item.active || hasActiveItem(item.children))
    }
    return hasActiveItem(props.item.children)
  })

  const emit = defineEmits(['click'])
</script>

<template>
  <RouterLink
    v-if="!item.children.length"
    :class="[
      'group flex w-full items-center rounded-md py-2 px-3 text-sm',
      'hover:bg-gray-100',
      item.active ? 'text-gray-800 font-semibold' : 'text-gray-600 font-medium',
    ]"
    :to="item.href"
    @click="emit('click', $event)"
  >
    <component
      :class="[
        'w-6 h-6 shrink-0 mr-2 group-hover:text-gray-600',
        item.active ? 'text-gray-600' : 'text-gray-400',
      ]"
      :is="item.icon"
      v-if="item.icon"
    ></component>
    <span>{{ item.label }}</span>
  </RouterLink>

  <Disclosure v-else v-slot="{ open }" :default-open="hasActiveChild">
    <DisclosureButton
      :class="[
        'group flex w-full text-left items-center rounded-md py-2 px-3 text-sm',
        'hover:bg-gray-100',
        open ? 'font-semibold text-gray-800' : 'font-medium text-gray-600',
      ]"
    >
      <component
        :class="[
          'w-6 h-6 shrink-0 mr-2 group-hover:text-gray-600',
          open ? 'text-gray-600' : 'text-gray-400',
        ]"
        :is="item.icon"
        v-if="item.icon"
      ></component>
      <span class="flex-1">{{ item.label }}</span>
      <ChevronDownIcon
        :class="['w-4 h-4 shrink-0', open ? '-rotate-180 text-gray-600' : 'text-gray-400']"
      />
    </DisclosureButton>
    <DisclosurePanel class="ml-4">
      <NavBarItem
        v-for="(child, index) in item.children"
        :item="child"
        :key="index"
        @click="emit('click', $event)"
      />
    </DisclosurePanel>
  </Disclosure>
</template>

我试图映射我从 navBarItem 中的发射返回的项目,但没有成功,有人可以帮我吗?

vue-component vuejs3 sidebar headless-ui
1个回答
0
投票

您在安装

navItems
后对
<Navbar />
执行的任何突变都不会触发
<template />
中的更新,因为数组不是反应性的。

要为其添加反应性,请将其包裹在

ref()

const navItems = ref([ /* items here */ ])

在您的

setActiveLink
方法中相应地更新
navItems.value
,您将看到模板相应地更新。

文档这里.


重要说明:仅当您想要单独处理每个项目的活动状态时(例如:您希望一次能够拥有多个活动项目),将活动属性存储在每个项目中才有意义。如果

active
状态是互斥的(例如:制作一个项目
active
意味着前一个不再处于活动状态),您 不应该 将活动状态存储在每个项目中。

相反,您设置一个状态变量来保存当前活动项目的

id
。每个项目的
isActive
属性是一个
computed
,它将项目的
id
与状态变量的值进行比较。显然,当将状态变量设置为不同的
id
时,旧的活动项变为非活动状态,因为它的
id
不再等于状态变量的值。

© www.soinside.com 2019 - 2024. All rights reserved.