Creating a 2 level navbar using Vue js - javascript

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>

Related

Responsive select menu does not display array values

I have a navbar that displays values depending on selected tabs
Now I want to do exactly the same in mobile view, for that, I have a select menu instead navbar
But value is not changing
But values are not changing when I select different value, what am I doing wrong? Regards
CodePen
Code:
<template>
<div>
<div class="sm:hidden">
<label for="tabs" class="sr-only">Select a tab</label>
<select id="tabs" name="tabs" class="block w-full focus:ring-indigo-500 focus:border-indigo-500 border-gray-300 rounded-md">
<option v-for="tab in tabs" :key="tab.name" :selected="tab.current">{{ tab.name }}</option>
</select>
<div >
<div v-for="tab in tabs" #click="changeTab(tab)" :key="tab.name" :href="tab.href" class="px-12" :class="[tab.current || 'hidden']">
{{ tab.id }} - {{ tab.name }} - {{ tab.href }} - {{ tab.title }} - {{tab.imageSrc}}
</div>
</div>
</div>
<div class="hidden sm:block">
<nav class="flex space-x-4 " aria-label="Tabs" >
<a v-for="tab in tabs" #click="changeTab(tab)" :key="tab.name" :href="tab.href" :class="[tab.current ? 'bg-purple-700 text-white' : 'text-purple-700 hover:text-gray-700', 'px-12 py-2 font-medium text-sm rounded-full font-bold text-lg']" >
{{ tab.name }}
</a>
</nav>
</div>
<div class="hidden sm:block">
<div v-for="tab in tabs" #click="changeTab(tab)" :key="tab.name" :href="tab.href" class="px-12" :class="[tab.current || 'hidden']">
{{ tab.id }} - {{ tab.name }} - {{ tab.href }} - {{ tab.title }} - {{tab.imageSrc}}
</div>
</div>
</div>
</template>
<script lang="ts">
import { ref } from 'vue'
export default {
setup() {
const tabs = ref([
{ id: 1 , title: 'test title one', imageSrc:'/programs/test1.png' , content: '', name: 'LOREM', href: '#test1', current: true },
{ id: 2 , title: 'test title two', imageSrc:'/programs/test2.png', content: '', name: 'IPSUM', href: '#test2', current: false },
{ id: 3 , title: 'test title three', imageSrc:'/programs/test3.png', content: '', name: 'PDF', href: '#test3', current: false },
{ id: 4 , title: 'test title three', imageSrc:'/programs/test3.png', content: '', name: 'PDF', href: '#test3', current: false },
{ id: 5 , title: 'test title three', imageSrc:'/programs/test3.png', content: '', name: 'PDF', href: '#test3', current: false },
{ id: 6 , title: 'test title three', imageSrc:'/programs/test3.png', content: '', name: 'PDF', href: '#test3', current: false },
])
const changeTab = (selectedTab) => {
tabs.value.map(t => {
t.id === selectedTab.id ? t.current = true : t.current = false
});
}
return { tabs, changeTab }
},
computed: {
img() {
return `./images/modal/${encodeURIComponent(this.tabs[0].imageSrc)}.png`
},
},
}
</script>
<style lang="scss" scoped>
nav {
display: flex;
gap: 20px;
background: #E5E5E5;
border-radius: 20px;
width: 100%;
justify-content: space-between;
}
</style>
Your <select> tag has no binding. Usually you should have a v-bind or at least a change handler to update the data.
Your change handler could look like this:
<select
#change="changeTab($event.target.value)"
>
...
</select>

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"))
}
`

Multilevel menu using children condition in Vue js

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.

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