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">
Item List 1
</li>
<li class="nav-item">
Item List 2
</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 ???
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">
{{subItem.name}}
</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.
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">
Item List 1
</li>
<li class="nav-item">
Item List 2
</li>
</ul>
</div>
</li>
https://www.codeply.com/p/GA5CaNMzmc
EDIT: I updated the demo to make it scaleable for multiple collapses
This is a fully working version using bootstrap-vue:
<div class="accordion" role="tablist">
<b-card v-for="(value, key) in this.jobs" :key="key" no-body class="mb-1">
<b-card-header header-tag="header" class="p-1" role="tab">
<b-button block v-b-toggle="'accordion-'+key" variant="primary">{{ value.title }}</b-button>
</b-card-header>
<b-collapse :id="'accordion-'+key.toString()" accordion="my-accordion" role="tabpanel">
<b-card-body>
<b-card-text>{{ value.specs }}</b-card-text>
</b-card-body>
</b-collapse>
</b-card>
</div>
Data object:
data() {
return {
jobs: [
{
title: 'Design artist',
specs: 'Have an eye for web beauty'
},
{
title: 'Backend guru',
specs: 'Do stuff that don\'t break'
},
{
title: 'Frontend master',
specs: 'Create an UI that works'
}
]
}
}
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;
})
}
Related
I have two layer navbar and the app is using Lazy loading, but every time I clcik on Team Management button the component is working but the Admin Panel navbar is dispear:
Here is my admin-routing.module.ts:
const routes: Routes = [
{ path: '', component: AdminComponent },
{
path: 'team',
loadChildren: () => import('./team/team.module').then((m) => m.TeamModule),
},
];
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class AdminRoutingModule {}
Here is HTML for the admin.component.html:
<div>
<nav class="navbar navbar-expand-sm navbar-light bg-light mb-3">
<div class="container">
<a class="navbar-brand" href="#">Admin Panel</a>
<button
class="navbar-toggler"
data-toggle="collapse"
data-target="#team-navbarNav"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="team-navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/team" routerLink="team"
>Team Management</a
>
</li>
<li class="nav-item">
<a class="nav-link" href="">Others</a>
</li>
</ul>
</div>
</div>
</nav>
</div>
<router-outlet></router-outlet>
Here is the result of after click on Team Management button:
As you can see the second navbar is gone, I do set up
<router-outlet></router-outlet>
in admin.component.html, but it feels like never works..
team is the sub component of admin.
admin-routing. module.ts:
Here is my Github repo:
https://github.com/qwe3804132/BusyQA-CRM/tree/main/CRMFrontEnd
any suggestions?
I am trying to build a sidebar that render menu dynamically depends on menu data provided.
Here is my TheSideBarMenu.vue
<template>
<ul class="nav flex-column pt-3 pt-md-0">
<li class="nav-item" v-for="m in menu" :key="m.path">
<router-link v-if="m.children.length===0" class="nav-link" :to="m.to">
<span class="sidebar-icon"><span :class="m.icon"></span></span>
<span class="sidebar-text">{{ m.label }}</span>
</router-link>
<span v-if="m.children.length > 0" class="nav-link collapsed d-flex justify-content-between align-items-center" data-bs-toggle="collapse" data-bs-target="#submenu-app">
<span>
<span class="sidebar-icon"><span class="fas fa-user"></span></span>
<span class="sidebar-text">Users</span>
</span>
<span class="link-arrow"><span class="fas fa-chevron-right"></span></span>
</span>
<div v-if="m.children.length > 0" class="multi-level collapse" role="list" id="submenu-app" aria-expanded="false">
<the-side-bar-menu :menu="m.children" />
</div>
</li>
</ul>
</template>
<script>
export default {
name: "TheSideBarMenu",
props: {
menu : Array
},
setup(props) {
console.log('sidebarmenu',props.menu)
},
}
</script>
and menu would like something like this
[
{
to : '/',
label : 'Dashboard',
icon : 'fas fa-chart-pie'
},
{
to : '#',
label : 'User',
icon : 'fas fa-user',
children : [
{
to : '/user',
label : 'User List',
icon : 'fas fa-chart-pie'
},
{
to : '/user/create',
label : 'Create User',
icon : 'fas fa-chart-pie'
}
]
},
{
to : '/search',
label : 'Search',
icon : 'fas fa-chart-pie'
}
]
The problem is when I put in a component template it call component's setup infinitely. Is my recursive component wrong or what?
I have been doing a project in which I need to create a dynamic drop down menu. In this project I am been using Angular 8 and bootstrap 4
The piece of code of html
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Dropdown
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<li ng-model="getOrders" ng-repeat="order in orders">
<a ng-click="getOrders()">{{orders}}</a>
//also tried {{ order.name }} - no result
</li>
<!-- <a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Something else here</a> -->
</div>
</li>
The above code is taken from the bootstrap site with the dropdown code:
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Dropdown
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</li>
and I have tried to use it with the below which I found in the Internet:
<span class="dropdown" dropdown on-toggle="toggled(open)">
<a href class="dropdown-toggle" dropdown-toggle>
Click me!
</a>
<ul class="dropdown-menu">
<li ng-repeat="choice in items">
<a ng-click="runFn(choice.fn)">{{choice.name}}</a>
</li>
</ul>
</span>
The code from component.ts from some tutorial:
form: FormGroup;
orders = [];
public someinfo:String = "";
public info:String = "";
constructor(private formBuilder: FormBuilder) {
this.form = this.formBuilder.group({
orders: ['']
});
this.orders = this.getOrders();
}
ngOnInit() {
}
getOrders() {
return [
{ id: '1', name: 'order 1' },
{ id: '2', name: 'order 2' },
{ id: '3', name: 'order 3' },
{ id: '4', name: 'order 4' }
];
}
After compiling the project and clicking the Dropdown Menu, the
result is like below:
[object Object],[object Object],[object Object],[object Object]
Could you please point me out, how should I fix it?
Thanks.
Can you try below?
Define how each order is going to look like outside your component class
interface OrderEntity
{
id : string;
name : string;
}
Inside you component class specify that, orders array will have the same structure
orders: OrderEntity[] = [];
With in getOrders try and push data into orders array
getOrders()
{
this.order.push({id : '1', name : 'order1'});
}
On the template you ca access orders as list and access each item by doing let item of orders; and obtain its attributes {{item.id}} and {{item.name}}
My vuejs application has a navigation bar and its code as bellow.
The problem is navigation bar display with its color but no links are available at the navigation bar .also console display a error like this click here to view the error . when I searched through the stackoverflow i found that some answers related to my problem and on was changing auth: '' to auth: {} . But it dosent also worked .So could any please help me to fix this.Thanks
<template>
<nav class="navbar navbar-expan-lg navbar-dar bg-primary rounded">
<button class="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbar1"
aria-controls="navbar1"
aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-md-center" id="navbar1">
<ul class="navbar-ul">
<li class="nav-item">
<router-link class="nav-link" to="/home">Home</router-link>
</li>
<li v-if="auth == ''" class="nav-item">
<router-link class="nav-link" to="/login">Login</router-link>
</li>
<li v-if="auth == ''" class="nav-item">
<router-link class="nav-link" to="/register">Register</router-link>
</li>
<li v-if="auth == 'loggedin'" class="nav-item">
<router-link class="nav-link" to="/profile">Profile</router-link>
</li>
<li v-if="auth == 'loggedin'" class="nav-item">
<router-link class="nav-link" to="/logout">Logout</router-link>
</li>
</ul>
</div>
</nav>
</template>
<script>
import EventBus from './EventBus'
export default {
data () {
return {
auth: '',
user: ''
}
},
methods: {
logout () {
localStorage.removeItem('usertoken')
}
},
mounted () {
EventBus.$on('logged-in', status => {
this.auth = status
})
}
}
This is the index.js of Router folder
import Vue from 'vue'
import Router from 'vue-router'
import Home from '#/components/Home'
import Login from '#/components/Login'
import Register from '#/components/Register'
import Profile from '#/components/Profile'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/register',
name: 'Register',
component: Register
},
{
path: '/profile',
name: 'Profile',
component: Profile
}
]
})
You are missing a to in your last link. Replace the href with to and you error should dissappear.
<li v-if="auth == 'loggedin'" class="nav-item">
<router-link class="nav-link" to="">Logout</router-link>
</li>
The error about name on undefined should be gone too. This is probably because it cannot find the a route with the key undefined as defined by the router-link
If this doesn't solve it, you may need to post more code.
Like the title says, Im trying to append a vue-component to an existing v-model
The only thing i found on the internet was the v-for solution to add more elements, but that is not what I want to do since this v-model could have multiple components
chat.vue
The V-model im trying to append to
<template>
<ul class="chat-ul" v-model="messages">
<chat-entry :name="name" :message="text" direction="left"></chat-entry>
</ul>
</template>
Here is where im stuck, how i can add the components
<script>
Vue.component('chat-entry', require('./entry.vue'));
module.exports = {
data: function () {
return {
name: 'Default name',
message: 'Default message',
messages: null,
}
},
mounted: function () {
// This is the what i want to achive
this.messages.append('chat-entry', {direction: 'left', message: 'hello', name: 'HeatON'});
this.$http.get('/etc/yelp.xi').then(response => {
for (let q in response.body.arr) {
this.messages.append('chat-entry', {direction: 'left', message: q.message, name: q.name});
}
});
}
}
entry.vue
<template>
<div v-if="direction == 'left'">
<li>
<div class="message-data">
<span class="message-data-name"><i class="fa fa-circle you"></i> {{ name }}</span>
</div>
<div class="message you-message"> {{ message }} </div>
</li>
</div>
<div v-else>
<li class="clearfix">
<div class="message-data align-right">
<span class="message-data-name">{{ name }}</span> <i class="fa fa-circle me"></i>
</div>
<div class="message me-message float-right"> {{ message }} </div>
</li>
</div>
</li>
Is something like this even possible? if not, what is the proper approach?
Thanks in advance