Multilevel menu using children condition in Vue js - javascript

This navbar has a 1 level dropdown menu. I want to create up to 3 levels but when adding a grandchildren condition to the children it won't work. With this setting, how would I be able to create a 3 level dropdown menu?
Component Header.vue
<template>
<!-- Top menu -->
<nav
id="mainNav"
:class="$route.path==='/home' ? '' : 'bg-primary'"
class="navbar navbar-expand-md navbar-dark fixed-top"
>
<!-- <div class="rtl-layout" #click="addToggleClass()">RTL</div>-->
<div class="container ">
<router-link class="navbar-brand" routerLinkActive="active-link" to="/home">
<!-- <img src="/static/img/zemle-logo.png" class="img-fluid" width="110" height="37">-->
<h3 class="text-white">Logo</h3>
</router-link>
<button
aria-controls="navbarCollapse" aria-expanded="true" aria-label="Toggle navigation" class="navbar-toggler"
data-target="#navbarCollapse" data-toggle="collapse" type="button">
<span class="navbar-toggler-icon"></span>
</button>
<div id="navbarCollapse" class="collapse navbar-collapse">
<ul id="main" class="navbar-nav ml-auto main-menu list-unstyled">
<li
v-for="(menuItem,i) in menus"
:id="'menuNo'+i"
:key="i"
class="parent nav-item"
#click="menuToggleLink('menuNo'+i);"
>
<router-link
v-if="menuItem.type == 'link'"
:to="menuItem.state"
class="nav-link"
#click.native="removeCollapseInClass();"
>
{{ menuItem.name }}
</router-link>
<a
v-if="menuItem.type == 'sub'"
class="nav-link"
href="javascript:void(0)"
>
{{ menuItem.name }}
<i :class="menuItem.icon" #click.stop="menuToggle('menuNo'+i)"></i>
</a>
<ul
v-if="menuItem.type == 'sub' && menuItem.children"
class="sub-menu arrow-up list-unstyled">
<li
v-for="(grandchildItem,i) of menuItem.children"
:key="i"
class="nav-item"
>
<router-link
v-if="grandchildItem.type == 'link'"
:to="grandchildItem.state"
class="nav-link"
#click.native="removeCollapseInClass()"
>
{{ grandchildItem.name }}
</router-link>
</li>
</ul>
</li>
<li>
<div
class="search-form"
click-outside
>
<span
data-target="#search-style-simple"
#click="showSearch()"
>
<i class="fa fa-search"></i>
</span>
<div
id="search-style-simple"
:class="{'search-active': searchactive}"
class="module-container"
>
<form action="javascript:void(0);" class="search-box" method="get" role="search">
<input class="form-control" name="" placeholder="Search" type="search" value=""/>
<button type="submit"><i class="fa fa-search"></i></button>
</form>
</div><!-- /module-container -->
</div><!-- /module -->
</li>
</ul>
</div>
</div>
</nav>
</template>
<script>
export default {
data() {
return {
searchactive: false,
menus: [
{
state: "/home",
name: "Home",
type: "link"
},
{
state: "",
name: "Products",
type: "sub",
icon: 'fa fa-caret-down',
children: [
{state: 'apparel', name: 'Apparel', type: "link"},
{state: 'bathroom', name: 'Bathroom', type: "link"},
{state: 'bedding', name: 'Bedding', type: "link"},
{state: 'decorative-details', name: 'Decorative Details', type: "link"},
]
},
{
state: "",
name: "Shop",
type: "sub",
icon: 'fa fa-caret-down',
children: [
{state: 'product-grid', name: 'Product Grid', type: "link"},
{ state: 'product-cart', name: 'Product Cart', type:"link"},
{ state: 'product-checkout', name: 'Product Checkout', type:"link"},
{state: 'product-detail', name: 'Product Detail', type: "link"}
]
},
{
state: "",
name: "Blog",
type: "sub",
icon: 'fa fa-caret-down',
children: [
{state: 'blog-column3', name: 'Blog Column1', type: "link"},
{state: 'blog-masonry3', name: 'Blog Masonry', type: "link"},
{state: 'blog-sidebar', name: 'Blog Sidebar', type: "link"},
{state: 'blog-detail', name: 'Blog Detail', type: "link"}
]
}
]
}
},
mounted() {
this.onScrollEvent();
},
methods: {
showSearch() {
this.searchactive = !this.searchactive;
},
menuToggleLink(id) {
if (document.getElementById(id).classList.contains("open")) {
document.getElementById(id).classList.remove("open");
} else if (!document.getElementById(id).classList.contains("open")) {
let elements = document.querySelectorAll(".parent");
for (var i = 0; i < elements.length; i++) {
elements[i].classList.remove('open');
}
document.getElementById(id).classList.add("open");
}
},
addToggleClass() {
document.querySelector("body").classList.toggle("rtl-enable");
},
removeCollapseInClass() {
document.getElementById("navbarCollapse").classList.remove('show');
},
onScrollEvent() {
var headerSticky = document.getElementById('mainNav');
window.onscroll = function () {
if (window.pageYOffset >= 100) {
headerSticky.classList.add("scrollHeader");
} else {
headerSticky.classList.remove("scrollHeader");
}
}
}
}
}
</script>
I have tried modifying the children and adding a grandchildren to it but it won't display the 2nd or 3rd level menu.

Related

VueDraggable send request to DB while drag and drop

I need a help for vuedraggable component. I have three columns. (Photo inserted) I want to drag BoardUserCard between columns and want to send PUT request to database for changing "lead_status_id" related to column dropped. How can I achieve this ? I can not find any example about apis in documentation.
<template>
<div class="board">
<div class="boards">
<div class="boards-cards leads">
<div class="board-card-header ">
<h3>
Leads
</h3>
<span>
{{ allLeads.length }}
</span>
</div>
<draggable
ghost-class="ghost"
:list="allLeads"
class="board-card-body"
:options = "{group:allLeads}"
group="leads"
#change="handleChange"
>
<BoardUserCard
v-for="item in allLeads"
:key="item.name"
:name="item.name"
:email="item.email"
:img="item.img"
:status="item.status"
/>
</draggable>
</div>
<div class="boards-cards contacted">
<div class="board-card-header ">
<h3>
Contacted
</h3>
<span>
{{ contactedLeads.length }}
</span>
</div>
<draggable
ghost-class="ghost"
:list="contactedLeads"
class="board-card-body"
:options = "{group:contactedLeads}"
group="leads"
#change="handleChange"
>
<BoardUserCard
v-for="item in contactedLeads"
:key="item.name"
:name="item.name"
:email="item.email"
:img="item.img"
:status="item.status"
/>
</draggable>
</div>
<div class="boards-cards converted">
<div class="board-card-header ">
<h3>
Converted
</h3>
<span>
{{ convertedLeads.length }}
</span>
</div>
<draggable
ghost-class="ghost"
:list="convertedLeads"
class="board-card-body"
:options = "{group:convertedLeads}"
group="leads"
#change="handleChange"
>
<BoardUserCard
v-for="item in convertedLeads"
:key="item.name"
:name="item.name"
:email="item.email"
:img="item.img"
:status="item.status"
/>
</draggable>
</div>
</div>
</div>
</template>
<script>
import BoardUserCard from "#/components/BoardUserCard.vue";
import draggable from "vuedraggable";
export default {
name: "Dashboard",
components: {
BoardUserCard,
draggable,
},
data() {
return {
showModal: false,
allLeads: [
{
name: "Richard",
email: "Richard#gmail.com",
img: "avatar-small.svg",
status: "all"
},
{ name: "Rachael", email: "Rachael#gmail.com", img: "leads.svg", status: "all" },
{ name: "Matt", email: "Matt#gmail.com", img: "user-avatar.svg",status: "all" },
{ name: "Brad", email: "Brad#gmail.com", img: "leads.svg", status: "all"},
],
contactedLeads: [
{
name: "Jeniffer",
email: "Jeniffer#gmail.com",
img: "avatar-small.svg",
status: "contacted"
},
],
convertedLeads: [
{ name: "Mike", email: "Mike#gmail.com", img: "leads.svg", status: "converted" },
],
};
},
methods: {
openModal() {
this.showModal = true;
},
closeModal() {
this.showModal = false;
},
handleChange(event){
console.log(event)
}
},
};
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
All I need to add data attribute in component data-id="4"
<draggable
ghost-class="ghost"
:list="convertedLeads"
class="board-card-body"
:options = "{group:convertedLeads}"
group="leads"
#end="handleChange"
data-id="4"
>
And access data-id attribute in handleChange function
handleChange(event){
console.log(event.from.getAttribute("data-id"))
console.log(event.to.getAttribute("data-id"))
}
`

Creating a 2 level navbar using Vue js

I am trying to create a 2 level navigation bar with this code but when I try to add another children to one of the children in the scrip it won't work. The idea is for the user to go to a second level where he can select other links. It should hover on link in navbar, then go to the 1st level menu and then hover on one of the option to get the 2nd level menu.
<template>
<!-- Top menu -->
<nav
id="mainNav"
class="navbar navbar-expand-md navbar-dark fixed-top"
:class="$route.path==='/home' ? '' : 'bg-primary'"
>
<!-- <div class="rtl-layout" #click="addToggleClass()">RTL</div>-->
<div class="container ">
<router-link class="navbar-brand" to="/home" routerLinkActive="active-link">
<!-- <img src="/static/img/zemle-logo.png" class="img-fluid" width="110" height="37">-->
<h3 class="text-white" >Sospes Logo</h3>
</router-link>
<button
class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="true" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul id="main" class="navbar-nav ml-auto main-menu list-unstyled">
<li
#click="menuToggleLink('menuNo'+i);"
class="parent nav-item"
v-for="(menuItem,i) in menus"
:id="'menuNo'+i"
:key="i"
>
<router-link
class="nav-link"
:to="menuItem.state"
v-if="menuItem.type == 'link'"
#click.native="removeCollapseInClass();"
>
{{ menuItem.name }}
</router-link>
<a
v-if="menuItem.type == 'sub'"
class="nav-link"
href="javascript:void(0)"
>
{{ menuItem.name }}
<i :class="menuItem.icon" #click.stop="menuToggle('menuNo'+i)"></i>
</a>
<ul
v-if="menuItem.type == 'sub' && menuItem.children"
class="sub-menu arrow-up list-unstyled" >
<li
class="nav-item"
v-for="(grandchildItem,i) of menuItem.children"
:key="i"
>
<router-link
class="nav-link"
:to="grandchildItem.state"
v-if="grandchildItem.type == 'link'"
#click.native="removeCollapseInClass()"
>
{{ grandchildItem.name }}
</router-link>
</li>
</ul>
</li>
<li>
<div
class="search-form"
click-outside
>
<span
data-target="#search-style-simple"
#click="showSearch()"
>
<i class="fa fa-search"></i>
</span>
<div
class="module-container"
:class="{'search-active': searchactive}"
id="search-style-simple"
>
<form role="search" method="get" class="search-box" action="javascript:void(0);">
<input type="search" class="form-control" placeholder="Search" value="" name="" />
<button type="submit"><i class="fa fa-search"></i></button>
</form>
</div><!-- /module-container -->
</div><!-- /module -->
</li>
</ul>
</div>
</div>
</nav>
</template>
<script>
export default{
data(){
return{
searchactive:false,
menus: [
{
state: "/home",
name: "Home",
type:"link"
},
{
state:"",
name:"Pages",
type:"sub",
icon: 'fa fa-caret-down',
children: [
{ state: 'about', name: 'About', type:"link"},
{ state: 'features', name: 'Features', type:"link"},
{ state: 'contact', name: 'Contact', type:"link"},
{ state: 'pricing', name: 'Pricing', type:"link"},
{ state: 'search', name: 'Search', type:"link"},
{ state: 'portfolio-v1', name: 'Products V1', type:"link"},
{ state: 'portfolio-v2', name: 'Products V2', type:"link"},
{ state: 'portfolio-v3', name: 'Products V3', type:"link"}
]
},
{
state:"",
name:"Features",
type:"sub",
icon: 'fa fa-caret-down',
children: [
{ state: 'login', name:'Login', type:"link"},
{ state: 'sign-up', name: 'Sign Up', type:"link"},
{ state: 'thank-you', name: 'Thank You', type:"link"},
{ state: 'maintenance', name: 'Maintenance', type:"link"},
{ state: 'not-found', name: '404', type:"link"}
]
},
{
state:"",
name:"Shop",
type:"sub",
icon: 'fa fa-caret-down',
children: [
{ state: 'product-grid', name:'Product Grid', type:"link"},
{ state: 'product-cart', name: 'Product Cart', type:"link"},
{ state: 'product-checkout', name: 'Product Checkout', type:"link"},
{ state: 'product-detail', name: 'Product Detail', type:"link"}
]
},
{
state:"",
name:"Blog",
type:"sub",
icon: 'fa fa-caret-down',
children: [
{ state: 'blog-listing-sidebar', name:'blog column ', type:"link"},
{ state: 'blog-column3', name: 'Blog Column1', type:"link"},
{ state: 'blog-masonry3', name: 'Blog Masonry', type:"link"},
{ state: 'blog-sidebar', name: 'Blog Sidebar', type:"link"},
{ state: 'blog-detail', name: 'Blog Detail', type:"link"}
]
}
]
}
},
mounted(){
this.onScrollEvent();
},
methods:{
showSearch(){
this.searchactive = !this.searchactive;
},
menuToggleLink(id){
if (document.getElementById(id).classList.contains("open")){
document.getElementById(id).classList.remove("open");
} else if(!document.getElementById(id).classList.contains("open")) {
let elements = document.querySelectorAll(".parent");
for (var i = 0; i < elements.length; i++) {
elements[i].classList.remove('open');
}
document.getElementById(id).classList.add("open");
}
},
addToggleClass(){
document.querySelector("body").classList.toggle("rtl-enable");
},
removeCollapseInClass(){
document.getElementById("navbarCollapse").classList.remove('show');
},
onScrollEvent(){
var headerSticky = document.getElementById('mainNav');
window.onscroll = function() {
if (window.pageYOffset >= 100) {
headerSticky.classList.add("scrollHeader");
} else {
headerSticky.classList.remove("scrollHeader");
}
}
}
}
}
</script>
<style>
.scrollHeader{
background-color: #0B3242;
}
</style>

Dropdown menu in VueJS (toggling of active menu)

This is the menu and below my navigation menu and method that on click should open the subitems inside nav item on click but I cant seem to be find where I'm lacking
[![dropdown_menu][1]][1]
new Vue({
el: '#app',
data: {
//menu
"menu_title": "a",
"child_routes": [{
"path": "/a1",
"menu_title": "a1"
},
{
"path": "/a2",
"menu_title": "a2"
}
]
},
{
"menu_title": "b",
"child_routes": [{
"path": "/b1",
"menu_title": "b1"
},
{
"path": "/b2",
"menu_title": "b2"
},
{
"path": "/b1",
"menu_title": "b3"
}
]
},
{
"menu_title": "c",
"child_routes": [{
"path": "/c1",
"menu_title": "c1"
},
{
"path": "/c2",
"menu_title": "c2"
},
{
"path": "/c3",
"menu_title": "c3"
}
]
}
},
methods: {
navlinks() {
var navItemParent = document.querySelector("nav-item");
var navLink = document.querySelector(".idb-nav .nav-item .nav-link");
var navItem = document.querySelector(".idb-nav .nav-item");
navItem.classList.toggle("active");
if (navItem.contains("active")) {
navItem.classList.remove("active");
navLink.classList.toggle("active");
}
}
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<nav class="idb-sidebar-nav">
<ul class="idb-nav list-unstyled m-15 p-0">
<template v-for="(menu, index) in sideBarLinks.routes">
<li class="nav-item" v-if="menu.child_routes!=null" :key="index">
<a href="javascript:void(0)" class="nav-link" #click="navlinks" >
<i :class="menu.menu_icon" class="menu-icon"></i>
<span class="menu-title">{{menu.menu_title}}</span>
</a>
<ul class="list-unstyled sub-menu">
<router-link
:to="subMenu.path"
tag="li"
v-for="(subMenu, key) in menu.child_routes"
:key="key"
>
<a href="javascript:void(0)" class="sub-menu-nav-link">
<span>{{subMenu.menu_title}}</span>
</a>
</router-link>
</ul>
</li>
<router-link
:key="index"
:to="menu.path"
tag="li"
class="nav-item"
v-else
>
<a class="nav-link">
<i :class="menu.menu_icon" class="mr-15"></i>
<span class="menu-title">{{menu.menu_title }}</span>
</a>
</router-link>
</template>
</ul>
</nav>
</div>
The classes toggling should be managed as part of the data. Try this and see if it works for you:
HTML
<div id="app">
<nav class="idb-sidebar-nav">
<ul class="idb-nav list-unstyled m-15 p-0">
<template v-for="(menu, index) in sideBarLinks.routes" :key="index">
<li class="nav-item"
v-if="menu.child_routes && menu.child_routes.length">
<a :class="[ 'nav-link', { 'active': menu.active } ]"
href="javascript:void(0);"
#click="toggleMenu(index)">
<i :class="menu.menu_icon" class="menu-icon"></i>
<span class="menu-title">{{menu.menu_title}}</span>
</a>
<ul class="list-unstyled sub-menu">
<router-link tag="li"
v-for="(subMenu, key) in menu.child_routes"
:key="key"
:to="subMenu.path">
<a href="javascript:void(0);" class="sub-menu-nav-link">
<span>{{subMenu.menu_title}}</span>
</a>
</router-link>
</ul>
</li>
<router-link v-else
:key="index"
:to="menu.path"
tag="li"
class="nav-item">
<a :class="[ 'nav-link', { 'active': menu.active } ]"
href="javascript:void(0);"
#click="toggleMenu(index)">
<i :class="menu.menu_icon" class="mr-15"></i>
<span class="menu-title">{{menu.menu_title }}</span>
</a>
</router-link>
</template>
</ul>
</nav>
</div>
routes.json
For improved readability and maintainability, you'd best be moving the list to separate file.
[
{
"menu_title": "a",
"child_routes": [
{
"path": "/a1",
"menu_title": "a1"
},
{
"path": "/a2",
"menu_title": "a2"
}
],
active: true // First item opened by default (if you like)
},
{
"menu_title": "b",
"child_routes": [
{
"path": "/b1",
"menu_title": "b1"
},
{
"path": "/b2",
"menu_title": "b2"
},
{
"path": "/b1",
"menu_title": "b3"
}
],
active: false
},
{
// ...
}
]
app.js
import * as routes from './routes.json';
const vm = new Vue({
el: '#app',
data() {
return {
sideBarLinks: {
routes
}
}
},
methods: {
toggleSidebar(index) {
this.sideBarLinks
.routes
.forEach((menu, i) => menu.active = (i === index));
}
}
});

Vue.js dynamic classes

I have some html using Vue.js here:
<div id="app">
<ul class="navbar-nav">
<li class="nav-item" v-for="tab in tabs">
{{ tab.name }}
</li>
</ul>
</div>
I have the javascript here:
var app = new Vue({
el: '#app',
data: {
tabs: [
{ name: "Home", active: "" },
{ name: "Challenges", active: "active" },
{ name: "Scoreboard", active: "" },
{ name: "About", active: "" }
]
}
});
I want to set class to tabs.active since I'm using bootstrap. How can I do that?
Add
v-bind:class="tab.active"
So your code should look like this:
var app = new Vue({
el: '#app',
data: {
tabs: [{
name: "Home",
active: ""
},
{
name: "Challenges",
active: "active"
},
{
name: "Scoreboard",
active: ""
},
{
name: "About",
active: ""
}
]
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ul class="navbar-nav">
<li class="nav-item" v-for="tab in tabs" v-bind:class="tab.active">
{{ tab.name }}
</li>
</ul>
</div>
Use a class binding like this:
<li class="nav-item" :class="{active: tab.active === 'active'}" v-for="tab in tabs">
new Vue({
el: '#app',
data: {
tabs: [
{ name: "Home", active: "" },
{ name: "Challenges", active: "active" },
{ name: "Scoreboard", active: "" },
{ name: "About", active: "" }
]
}
});
.active .nav-link {
color: white;
background: blue;
}
<script src="https://unpkg.com/vue#2.5.17"></script>
<div id="app">
<ul class="navbar-nav">
<li class="nav-item" :class="{active: tab.active === 'active'}" v-for="tab in tabs">
{{ tab.name }}
</li>
</ul>
</div>

Vue Dynamic List

I'm creating a basic MTG Deckbuilder using Vue.Js. I'm trying to have the user type the card name into an input box then when the button is clicked, the deck list is update with the card name. Currently,the list add's a blank element for the card instead of the card name. Any ideas?
app.Vue:
<template>
<div id="app">
<deckBuilder #addCard="addCard" :title="title" :card="card" :deck="deck" />
</div>
</template>
<script>
import deckBuilder from './components/deckBuilder'
export default {
name: 'app',
components: {
deckBuilder,
},
data: () => ({
title: 'MTG Deck Builder',
card: {
name: '',
},
deck:[],
}),
methods: {
addCard: function(event,) {
this.deck.push(this.card.name);
},
},
}
</script>
deckBuilder.Vue:
<template>
<div>
<h1>{{ title }}</h1>
<input v-model="card.name" placeholder="Type a Card Name" />
<p> the card is: {{ card.name }}
<button #click="addCard">Add!</button>
<h2>Deck</h2>
<ul>
<li v-for="item in deck">{{ item.card }}</li>
</ul>
</div>
</template>
<script>
export default {
name: 'deckBuilder',
props: ['title', 'card', 'deck'],
methods: {
addCard() {
this.$emit('addCard')
}
},
}
</script>
Because you push string item instead of object
this.deck.push(this.card.name);
Correct template will be {{ item }}, not {{ item.card }}
This is because you are not emitting any data in your $emit
Try this,
In deckbuilder.vue
addCard() {
this.$emit('addCard',this.card.name);
}
In app.vue
addCard: function(cardName){
this.deck.push(cardName);
}
In html, v-for should be this way
<li v-for="item in deck">{{ item }}</li>
Try This
<!-- Menu Content -->
<ul class="nav-list" style="overflow: visible;">
<!-- Items -->
<span v-for="(menuItem, index) in menuItems" :key="index">
<li>
<nuxt-link :to="menuItem.link" :class="menuItem.class">
<i class="bx" :class="menuItem.icon || 'bx-square-rounded'" />
<!-- Link Name -->
<span class="links_name">{{ menuItem.name }}</span>
</nuxt-link>
</li>
</span>
</ul>
<script>
export default {
name: 'SidebarMenu',
menuItems: {
type: Array,
default: () => [
{
link: '/',
name: '_home',
icon: 'bx-home',
class: '',
},
{
link: '/blog',
name: '_blog',
icon: 'bx-edit',
class: '',
},
{
link: '/projects',
name: '_projects',
icon: 'bx-code',
class: ''
},
{
link: '/about',
name: '_about',
icon: 'bx-user',
class: ''
},
],
},
}
</script>

Categories

Resources