how to make bootstrap4 collapse work in vue?

旧巷老猫 提交于 2020-11-29 19:16:46

问题


I am using bootstrap 4.3.1 and vue@2.6.10

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 ???


回答1:


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.




回答2:


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




回答3:


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;
            })
        }



来源:https://stackoverflow.com/questions/58766932/how-to-make-bootstrap4-collapse-work-in-vue

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!