vue+iView dynamic sidebar menu remains highlighted

There are some pitfalls in the use of iview components. This article briefly lists them:

Pitfall avoidance guide: About the iview sidebar menu failure to expand and highlight the echo issue
Application scenario: After accessing the dynamic menu under iview-admin, the echo fails when refreshing or jumping into a link.

The simplicity lies in two aspects: before the dynamic menu is connected and after the dynamic menu is connected, the menu remains highlighted.

1. Before accessing the dynamic menu:

There is an article in this blog that briefly introduces the implementation method of iView jumping submenu and keeping the parent menu highlighted:

:active-name="$route.name"
 <side-menu
          accordion
          ref="sideMenu"
          :active-name="$route.name"
          :collapsed="collapsed"
          @on-select="turnToPage"
          :menu-list="menuList">
 </side-menu>

Menu remains highlighted

1. Method one:

The name of the page before jumping remains the same, and the meta of the jumping page is added.

recoverName: user-info’ // Keep the same name as the page before jumping.

2. Method two:

:active-name="activeName()"
<side-menu
        accordion
        ref="sideMenu"
        :active-name="activeName()"
        :collapsed="collapsed"
        @on-select="turnToPage"
        :menu-list="menuList"
      >

Add method

activeName() {
          if (this.$route.meta.activeName){
               return this.$route.meta.activeName;
          }else{
               return this.$route.name;
          }
     },

There is no problem with either method, but there is a problem when using dynamic menus with access permissions:

When you refresh the page or use a link to jump in, you will be redirected to the homepage, the parent menu will be retracted, and the submenu will not remain selected and highlighted.

2. After the dynamic menu is accessed:

Another article in this blog also briefly introduces iview dynamic rendering to display the sidebar menu and obtain selected data. The rendering method is:

:menu-list="menuList"

The main reason is that after the dynamic menu is connected, the menu highlight will become invalid.

The interface in mounted in main.vue dynamically obtains menu data and renders the sidebar menu.

 store.dispatch('getMenuData').then(res => {
  }

Solution to the problem of menu highlighting failure:

We can pass the method iview menu updateOpened updateActiveName property:

updateOpened()
updateActiveName()
this.$refs.sideMenu.updateOpenName(this.$route.name);

Called after a menu request, but must be used in $nextTick to be effective

 store.dispatch('getMenuData').then(res => {
          if(this.$store.getters.menuList){
                this.$nextTick(() => {
                this.$refs.sideMenu.updateOpenName(this.$route.name);
                });
          }
    })

In this way, regardless of jump or forced refresh, the sidebar menu will expand, but the selected submenu
Still no highlight selected echo

You can add the updateActiveName attribute method to the side-menu sidebar menu component to update it.

this.$refs.menu.updateActiveName() //Keep the highlight selected
updateOpenName (name) {
      if (name === this.$config.homeName) this.openedNames = []
      else this.openedNames = this.getOpenedNamesByActiveName(name)
      if(this.activeName){
          this.$nextTick(() => {
           this.$refs.menu.updateActiveName() //Keep the highlight selected
        })
      }
    },

3. Some main codes:

1.main.vue:

import SideMenu from "./components/side-menu";
 
      <side-menu
        accordion
        ref="sideMenu"
        :active-name="activeName()"
        :collapsed="collapsed"
        @on-select="turnToPage"
        :menu-list="menuList"
      >
      
    

2. In the side-menu component

<template>
  <div class="side-menu-wrapper">
    <slot></slot>
    <Menu ref="menu" v-show="!collapsed" :active-name="activeName" :open-names="openedNames" :accordion="accordion" :theme="theme" width="auto" @on -select="handleSelect">
      <template v-for="item in menuList">
        <template v-if="item.children & amp; & amp; item.children.length === 1">
          <side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
          <menu-item v-else :name="getNameOrHref(item, true)" :key="`menu-${item.children[0].name}`"><common-icon :type="item.children [0].icon || ''"/><span>{<!-- -->{ showTitle(item.children[0]) }}</span></menu-item>
        </template>
        <template v-else>
          <side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
          <menu-item v-else :name="getNameOrHref(item)" :key="`menu-${item.name}`"><common-icon :type="item.icon || ''"/> <span>{<!-- -->{ showTitle(item) }}</span></menu-item>
        </template>
      </template>
    </Menu>
    <div class="menu-collapsed" v-show="collapsed" :list="menuList">
      <template v-for="item in menuList">
        <collapsed-menu :center="true" v-if="item.children & amp; & amp; item.children.length > 1" @on-click="handleSelect" hide-title :root-icon-size= "rootIconSize" :icon-size="iconSize" :theme="theme" :parent-item="item" :key="`drop-menu-${item.name}`"></collapsed-menu>
        <Tooltip transfer v-else :content="showTitle(item.children & amp; & amp; item.children[0] ? item.children[0] : item)" placement="right" :key="`drop- menu-${item.name}`">
          <a @click="handleSelect(getNameOrHref(item, true))" class="drop-menu-a" :style="{textAlign: 'center'}"><common-icon :right="0" :size ="item.size" :color="textColor" :type="item.icon || (item.children & amp; & amp; item.children[0].icon)"/></a>
        </Tooltip>
      </template>
    </div>
  </div>
</template>
<script>
import SideMenuItem from './side-menu-item.vue'
import CollapsedMenu from './collapsed-menu.vue'
import { getUnion } from '@/libs/tools'
import mixin from './mixin'
export default {
  name: 'SideMenu',
  mixins: [ mixin ],
  components: {
    SideMenuItem,
    CollapsedMenu
  },
  props: {
    menuList: {
      type: Array,
      default () {
        return []
      }
    },
    collapsed: {
      type: Boolean
    },
    theme: {
      type: String,
      default: 'dark'
    },
    rootIconSize: {
      type: Number,
      default: 20
    },
    iconSize: {
      type: Number,
      default: 16
    },
    accordion: Boolean,
    activeName: {
      type: String,
      default: ''
    },
    openNames: {
      type: Array,
      default: () => []
    }
  },
  data () {
    return {
      openedNames: [],
    }
  },
  methods: {
    handleSelect (name) {
      this.$emit('on-select', name)
    },
    getOpenedNamesByActiveName (name) {
      return this.$route.matched.map(item => item.name).filter(item => item !== name)
    },
    updateOpenName (name) {
      if (name === this.$config.homeName) this.openedNames = []
      else this.openedNames = this.getOpenedNamesByActiveName(name)
      if(this.activeName){
          this.$nextTick(() => {
           this.$refs.menu.updateActiveName()
        })
      }
    },
  },
  computed: {
    textColor () {
      return this.theme === 'dark' ? '#fff' : '#495060'
    }
    
  },
  watch: {
    activeName (name) {
      if (this.accordion) this.openedNames = this.getOpenedNamesByActiveName(name)
      else this.openedNames = getUnion(this.openedNames, this.getOpenedNamesByActiveName(name))
    },
    openNames (newNames) {
      this.openedNames = newNames
    },
    openedNames () {
      this.$nextTick(() => {
        this.$refs.menu.updateOpened()
      })
    }
  },
  mounted () {
    this.openedNames = getUnion(this.openedNames, this.getOpenedNamesByActiveName(name))
     
  }
}
</script>
<style lang="less">
@import './side-menu.less';
</style>

More highlights from the past:

1.vue + iView permission practice dynamically display sidebar menu
2.vue + iView jumps to the submenu and keeps the parent menu highlighted
3.vue + iView tree menu echo and selection
4.vue + iView implements import and export excel functions
5.vue + iView table paging check memory function

If you like it, please like and follow.