By calin24


2019-11-08 12:48:28 8 Comments

I am using bootstrap 4.3.1 and [email protected]

I have this menu (is using collapse - and I don`t want to use JQuery):

 <li class="nav-item">
     <a class="nav-link" href="#sidebar-products" data-toggle="collapse" role="button" aria-expanded="false" aria-controls="sidebar-products">
         <i class="ni ni-single-copy-04 text-primary"></i>
         <span class="nav-link-text">Products</span>
     </a>
     <div class="collapse" id="sidebar-products">
         <ul class="nav nav-sm flex-column">
             <li class="nav-item">
                 <a href="#" class="nav-link">Item List 1</a>
             </li>
             <li class="nav-item">
                 <a href="#" class="nav-link">Item List 2</a>
             </li>
         </ul>
     </div>
 </li>

This is only a single block that contains 2 sub-items.

What I saw using JQuery, when click on "Products" the #sidebar-products receives the .show class and aria-expanded="true".

When having multiple blocks - when click on a block to close (if there are collapsed) the others blocks.

How can I make it work the collapse with vue?

UPDATE 1

I created a click event that do the job:

<a class="nav-link" href="javascript:void(0)" @click="navItemCollapse('sidebar-products', $event)" data-toggle="collapse" role="button" aria-expanded="false" aria-controls="sidebar-products">

and the event:

 navItemCollapse(id, event) {
     let expanded = event.target.getAttribute('aria-expanded').toLocaleLowerCase() == 'true' ? true : false;
     let el = document.getElementById(id);
     expanded ? el.classList.remove('show') : el.classList.add('show');
                event.target.setAttribute('aria-expanded', !expanded);
 }

But what if I have more blocks ? When click to open the current collapse on a block to close the others ???

3 comments

@calin24 2019-11-08 22:07:20

I like the @sugars approach :)

So...the final version is this:

<li v-for="(navItem, i) in sidenavItems" class="nav-item">
                            <router-link v-if="!navItem.isCollapsible" class="nav-link" @click.native="navItemCollapse(i)" active-class="active" :to="{name: navItem.route}" exact>
                                <i :class="navItem.class"></i>
                                <span class="nav-link-text">{{ navItem.name }}</span>
                            </router-link>

                            <a v-if="navItem.isCollapsible" class="nav-link" href="javascript:void(0)" @click="navItemCollapse(i)" data-toggle="collapse" :aria-expanded="navItem.expanded">
                                <i :class="navItem.class"></i>
                                <span class="nav-link-text">{{ navItem.name }}</span>
                            </a>
                            <div v-if="navItem.isCollapsible" class="collapse" :class="navItem.expanded ? 'show' : ''">
                                <ul class="nav nav-sm flex-column">
                                    <li v-for="subItem in navItem.items" class="nav-item">
                                        <router-link class="nav-link" :to="{name: subItem.route}">{{ subItem.name }}</router-link>
                                    </li>
                                </ul>
                            </div>
                        </li>

the sidenavItems:

 sidenavItems: [
                    {name: 'Dashboard', isCollapsible: false, route: 'dashboard', class: 'class1'},
                    {name: 'Categories', isCollapsible: false, route: 'category', class: 'class2'},
                    {name: 'Brands', isCollapsible: false, route: 'brand', class: 'class3'},
                    {name: 'Products', isCollapsible: true, expanded: false, class: 'class4', items: [{name: 'List', route: 'product'}]},
                    {name: 'Orders', isCollapsible: false, route: 'order', class: 'class5'},
                    {name: 'Blog', isCollapsible: true, expanded: false, class: 'class6', items: [{name: 'List', route: ''}]},
                ],

and the navItemCollapse method:

 navItemCollapse(index) {
     this.sidenavItems = this.sidenavItems.map( (item, i) => {
                item.expanded = !item.expanded;
                if(i !== index) {
                    item.expanded = false;
                }
                return item;
            })
        }

@sugars 2019-11-09 05:32:39

If it helps you, I hope to adopt my answer. Have a nice day :)

@Zim 2019-11-08 14:52:37

No jQuery or bootstrap-vue ...

Create a function in the Component to handle the normal Bootstrap class and timing logic...

  data() {
    return {
      classArr: ['collapse'],
      styleObj: {}
    };
  },
  methods: {
    toggleCollapse(ref) {
        let show = this.classArr.indexOf('show')>-1?false:'show'
        this.classArr = ['collapsing']
        setTimeout(() => {
            if (show){
                let height = this.$refs[ref].firstChild.clientHeight + 'px';
                this.styleObj = { height }
            }
            else {
                this.styleObj = {}  
            }
        }, 10)
        setTimeout(() => {
            this.classArr = ['collapse', show]
        }, 340)
    }
  }

In the component template, bind the class and style attrs to the data manipulated by the method. The ref of the specific collapse is passed in to the method...

  <li class="nav-item">
        <a class="nav-link" href="#sidebar-products" role="button" @click="toggleCollapse('sidebar')">
            <i class="ni ni-single-copy-04 text-primary"></i>
            <span class="nav-link-text">Products</span>
        </a>
        <div :class="classArr" :style="styleObj" id="sidebar-products" ref="sidebar">
            <ul class="nav nav-sm flex-column">
                <li class="nav-item">
                    <a href="#" class="nav-link">Item List 1</a>
                </li>
                <li class="nav-item">
                    <a href="#" class="nav-link">Item List 2</a>
                </li>
            </ul>
        </div>
  </li>

https://www.codeply.com/p/GA5CaNMzmc

EDIT: I updated the demo to make it scaleable for multiple collapses

@sugars 2019-11-08 14:57:07

This is the implementation of no jquery

new Vue({
  el: '#app',
  data() {
    return {
      menuList: [{
          name: 'Products',
          expand: false,
          items: [{
              name: 'Item List 1',
              link: ''
            },
            {
              name: 'Item List 2',
              link: ''
            }
          ]
        },
        {
          name: 'Others',
          expand: false,
          items: [{
              name: 'Other Item 1',
              link: ''
            },
            {
              name: 'Other Item 2',
              link: ''
            }
          ]
        }
      ]
    }
  },
  methods: {
    navItemCollapse(index) {
      this.menuList = this.menuList.map((item, i) => {
        item.expand = !item.expand
        if (i !== index) {
          item.expand = false
        }
        return item
      })
    }
  }
})
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>

<ul id="app">
  <li v-for="(navItem,i) in menuList" :key="i" class="nav-item">
    <a class="nav-link" href="javascript:;" data-toggle="collapse" role="button" :aria-expanded="navItem.expand" aria-controls="sidebar-products" @click.prevent="navItemCollapse(i)">
      <i class="ni ni-single-copy-04 text-primary"></i>
      <span class="nav-link-text">{{navItem.name}}</span>
    </a>
    <div v-if="navItem.items.length>0" class="collapse" :class="{show: navItem.expand}">
      <ul class="nav nav-sm flex-column">
        <li v-for="(subItem,j) in navItem.items" :key="j" class="nav-item">
          <a href="#" class="nav-link">{{subItem.name}}</a>
        </li>
      </ul>
    </div>
  </li>
</ul>

I integrate the menu data into an array of objects. Each object has an expand flag to determine whether it is currently expanded. When you click on the menu, switch the expand flag of the current menu.

Note: You don't need to care about the id of the <a> tag.

Related Questions

Sponsored Content

1 Answered Questions

vuebootstrap b-collapse: when sidebar collapse, change margin-left content div

1 Answered Questions

[SOLVED] Bootstrap Vue accordion not expanding with id from array

1 Answered Questions

Toggling active nav items with BootstrapVue and VueJs

1 Answered Questions

[SOLVED] @click not registering event in Vue devtools

  • 2019-03-24 22:09:24
  • Jim Strother
  • 90 View
  • 0 Score
  • 1 Answer
  • Tags:   javascript vue.js

1 Answered Questions

[SOLVED] NativeScript-Vue Update Data Object?

1 Answered Questions

4 Answered Questions

[SOLVED] Custom Vue dropdown maintain focus

1 Answered Questions

1 Answered Questions

[SOLVED] Vue Component Not Updating Data on Function

Sponsored Content