How to toggle between components when onclick function happens on a icon in vue.js? - vue.js

I have four components Dashboard.vue(parent component) it contains Three child components(DisplayBooks.vue,sortBooksHightoLow,sortBooksLowtoHigh).
Now i want to import one more component called Cart.vue inside the Dashboard.vue .By default only DisplayBooks.vue component is only visible,if i click on cart-icon inside the Dashboard component it displays the Cart.vue component and hides the DisplayBooks.vue component .
How to acheive this thing please help me to fix this thing..
Dashboard.vue
<template>
<div class="main">
<div class="navbar navbar-default navbar-fixed-top">
<div class="navbar-header">
<img src="../assets/education.png" alt="notFound" class="education-image" />
</div>
<ul class="nav navbar-nav">
<li>
<p class="brand">Bookstore</p>
</li>
</ul>
<div class="input-group">
<i #click="handlesubmit();" class="fas fa-search"></i>
<div class="form-outline">
<input type="search" v-model="name" class="form-control" placeholder='search...' />
</div>
</div>
<ul class="nav navbar-nav navbar-right" id="right-bar">
<li><a> <i class="far fa-user"></i></a></li>
<p class="profile-content">profile</p>
<li><a><i class="fas fa-cart-plus"></i></a></li>
<p class="cart-content" >cart <span class="length" v-if="booksCount">{{booksCount}}</span></p>
</ul>
</div>
<div class="mid-body">
<h6>Books<span class="items">(128items)</span></h6>
<select class="options" #change="applyOption">
<option disabled value="">Sort by relevance</option>
<option value="HighToLow">price:High to Low</option>
<option value="LowToHigh">price:Low to High</option>
</select>
</div>
<div v-if="flam==false">
<h2>Hello</h2>
</div>
<DisplayBooks v-show="flag==='noOrder'" #update-books-count="(n)=>booksCount=n"/>
<sortBooksLowtoHigh v-show="flag==='lowToHigh'" />
<sortBooksHightoLow v-show="flag==='highToLow'" />
<Cart />
</div>
</template>
<script>
import sortBooksLowtoHigh from './sortBooksLowtoHigh.vue'
import sortBooksHightoLow from './sortBooksHightoLow.vue'
import DisplayBooks from './DisplayBooks.vue'
import Cart from './Cart.vue'
export default {
components: {
DisplayBooks,
sortBooksLowtoHigh,
sortBooksHightoLow,
Cart
},
data() {
return {
booksCount:0,
flag: 'noOrder',
brand: 'Bookstore',
name: '',
flam: true,
visible: true,
}
},
methods: {
flip() {
this.flam = !this.flam;
},
applyOption(evt) {
if (evt.target.value === "HighToLow") {
this.flag = 'highToLow';
} else this.flag = 'lowToHigh';
},
}
}
</script>
Cart.vue
<template>
<div class="main">
<div v-for="book in books" :key="book.id" class="container">
<div class="content">
<h5>My Cart({{booksCount}})</h5>
</div>
<div class="mid-section">
<img v-bind:src="book.file" alt="not found">
<p class="title-section">{{book.name}}</p>
</div>
<div class="author-section">
<p>by {{book.author}}</p>
</div>
<div class="price-section">
<h6>Rs.{{book.price}}</h6>
</div>
<button class="close-btn" #click="handlesubmit();" type="submit">close</button>
<div class="btn-grps">
<button class="btn" type="submit" >Place Order</button>
</div>
</div>
</div>
</template>
<script>
import service from '../service/User'
export default{
data(){
return {
booksCount:0,
books: [{
id: 0,
file: 'https://images-na.ssl-images-amazon.com/images/I/41MdP5Tn0wL._SX258_BO1,204,203,200_.jpg',
name: 'Dont Make me think',
author: 'Sai',
price: '1500'
},]
}
},
methods:{
}
}
</script>

It's recommended to use vur-router, but for a simple situation you could define a property called shownComp then update when you click on cart icon :
data() {
return {
shownComp:'DisplayBooks',
booksCount:0,
flag: 'noOrder',
then <li #click="shownComp='Cart'"><a><i class="fas fa-cart-plus"></i></a></li>
and :
<DisplayBooks v-show="flag==='noOrder' && shownComp==='DisplayBooks'" #update-books-count="(n)=>booksCount=n"/>
<Cart v-show=" shownComp==='Cart'" />
...

Related

How to change the dropdown item in vue after login successful

I have a navbar and login components in vue. So my target is when if the user login, on the dropdown in navbar, I want to show Profile as an item, and if the user is not login I want to show Sign In/Register item as an item. So I want to manipulate the dropdown that you see on the picture below:
And here is my Navbar component:
<template>
<nav class="navbar navbar-expand-lg shadow">
<div class="container navbar-container">
<div class="navbar navbar-profile">
<div class="dropdown">
<button class="btn dropdown-toggle" type="button" id="dropdownProfile" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<i class="fa fa-fw fa-user"></i>
</button>
<ul class="dropdown-menu dropdown-menu-left">
<!-- User is not authenticated so show login/register-->
<li v-if="authenticated"><router-link :to="{ name: 'login' }">Login/Register</router-link></li>
<!-- User is authenticated so show profile-->
<li v-if="authenticated"><router-link :to="{ name: 'profile' }">Profile</router-link></li>
</ul>
</div>
</div>
</div>
</nav>
</template>
<script>
export default {
data() {
return {
authenticated: false,
authenticationFailed: false,
};
},
};
</script>
So in the code I didnt make anything with the authenticated/authenticationFailed values because I dont know how can I check it from the Login component. And here is my login component:
<template>
<div id="login" class="card-container">
<div class="card col-lg-6 col-md-6 col-sm-12 col-xs-12">
<div class="headers">
<h3 class="card-header text-center active">LOGIN</h3>
<router-link :to="{name: 'register'}"><h4 class="card-header text-center non-active">REGISTER</h4></router-link>
</div>
<div class="card-body">
<div class="form-row">
<div class="form-group">
<input type="text" class="form-control" v-model="email" placeholder="Email" id="email" #keyup.enter="signIn" >
</div>
<div class="form-group">
<input type="password" class="form-control" v-model="password" placeholder="Password" id="password" #keyup.enter="signIn">
</div>
<div class="form-group form-check">
<input type="checkbox" id="rememberMeSignIn" class="form-check-input">
<label class="form-check-label" for="rememberMeSignIn">Remember Me</label>
</div>
<div class="form-group links">
<router-link :to="{name: 'forget-password'}">Forget Password?</router-link>
</div>
</div>
<div class="button-area">
<button v-on:click="signIn()" type="submit" class="btn button">Sign In</button>
</div>
</div>
</div>
</div>
</template>
<script>
import authHelper from "../helpers/authHelper";
import GENERAL_APP_CONSTANTS from "../constants/GeneralAppConstants";
export default {
name: 'Login',
beforeMount() {
this.authenticated = authHelper.validAuthentication();
},
mounted() {
EventBus.$on(GENERAL_APP_CONSTANTS.Events.CheckAuthentication, () => {
this.authenticated = authHelper.validAuthentication();
if (this.authenticated) {
this.email = this.password = "";
this.authenticationFailed = false;
}
});
EventBus.$on(GENERAL_APP_CONSTANTS.Events.LoginFailed, () => {
this.authenticationFailed = true
});
},
data () {
return {
authenticated: false,
authenticationFailed: false,
email: '',
password: '',
rememberMe: false,
}
},
methods: {
signIn: function () {
authHelper.signIn(this.email, this.password, () => {
this.$router.push({name: 'home'});
});
}
}
}
</script>
I tried to make it as simple as possible and if you can help me with this , I would be really glad.
PS: this is my index.vue that you can see the relation between Navbar and Login page and my login component is displaying when router-view changed:
<div id="app">
<Topnav/>
<Navbar/>
<router-view></router-view>
<Footer/>
</div>

Bind data with router-link in the vue

I have declared a data variable inside my component. There is a select box that dynamically changes this data value. what I want to use this data value inside my router-link. but somehow it is not working.
Here is my code for the same:
<template>
<div class="row bg-blue content-padding pdt-70 relative d-flex">
<div class="col-md-8">
<div class="row mgb-60">
<form>
<div class="col-8 form-group mgb-30">
<label for="work-profile" class="color-white">Work Profile*</label>
<select id="work-profile" v-model="page" name="work-profile" class="form-control">
<option value="self-employed">Self Employed</option>
<option value="salaried">Salaried</option>
</select>
</div>
<div class="col-12 form-group">
<router-link :to=["/"+page]>
<button type="submit" class="btn form-button button-blue d-flex-inline justify-content-center align-items-center color-white bg-blue">Get Started</button>
</router-link>
</div>
</form>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "LandingPage",
data: function()
{
return{
page:'salaried'
}
},
components: {
},
};
</script>
Replace <router-link :to=["/"+page]> to <router-link :to="'/'+page"> you do not need [] with binding to

When console.log(this.selectedValue) I'm getting a null

I'm trying to get the value of my select but it isn't being picked up and I'm not sure why I've got v-model on my select tag and as far as I know it should have been picked up.
When I check my console I have a null.
Here is my code.
<template>
<div class="content-header">
<div class="container">
<div class="row">
<div class="col-sm-6">
<div class="card">
<div class="card-body">
<div class="row mb-2">
<div class="col-sm-12">
<label for="product_select">Products</label>
<select id="product_select" class="form-control" v-model="selectedValue">
<optgroup v-for="product in products" :label="product.text">
<option v-for="list in product.children" :value="list.id">{{ list.text }}</option>
</optgroup>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default{
props: ['products'],
data(){
return {
selectedValue: null
}
},
computed:{
},
watch: {
},
methods: {
}
mounted() {
console.log(this.selectedValue);
}
}
</script>
mounted is executed before your 'select' operation.

Pagination in vue.js framework

Error with Pagination in vue.js framework in view page, The Pagination bar is working fine but I have 50 records on the same page. The Pagination bar is 17 => 50/3 . The Post is displayed in div section not in Table.
<template>
<div class="blog">
<h2>{{ pageDescreption }}</h2>
<hr />
<div class="alert alert-success" role="alert" v-text="alertTitel"></div>
<div class="row">
<div class="col-md-8">
<div class="posts-area">
<PostList
id="PostList"
v-for="post in posts"
:key="post.id"
:post="post"
:current-page="currentPage"
:per-page="perPage"
/>
<div class="overflow-auto">
<b-pagination
v-model="currentPage"
:total-rows="rows"
:per-page="perPage"
aria-controls="PostList"
></b-pagination>
<p class="mt-3">Current Page: {{ currentPage }}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import PostList from "#/components/Blogs/PostList.vue";
import PostJson from "../Gatewaye/post.json";
export default {
data: function() {
return {
pageName: "blog",
pageDescreption: "This is an Blog page",
alertTitel: "List of all Posts",
posts: PostJson, // array of posts [50 records]
perPage: 3,
currentPage: 1
};
},
name: "Blog",
components: {
PostList
},
computed: {
rows() {
return this.posts.length;
}
}
};
</script>
And in Components file Blog.vue is:
<template>
<div class="PostList">
<div class="post-container text-left">
<span class="post-views badge badge-primary" title="Views">{{
post.views
}}</span>
<h3 class="post-title" title="Title">{{ post.title }}</h3>
<span class="post-date" title="Date">{{ post.date }}</span>
<p class="post-body">
{{ post.contant }}
</p>
<div class="row">
<div class="col-sm-6">
<span class="post-author" title="Auther">{{ post.auther }}</span>
</div>
<div class="col-sm-6 text-right">
<span class="post-category" title="Category">{{
post.category
}}</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ["post"],
name: "PostList"
};
</script>
The Pagination bar is working fine but I have 50 records on the same page ... Thank you
Use a computed property to slice the post array in the parent component:
computed: {
paginatedposts() {
return this.posts.slice(this.perPage*(this.currentPage-1), (this.perPage*(this.currentPage-1))+this.perPage);
}
}
Now you just need to bind this computed property:
<PostList
id="PostList"
v-for="post in paginatedposts"
:key="post.id"
:post="post"
/>

How do I make the hoverable dropdown close when navigating to a differerent link in Bulma?

HERE is a SANDBOX with the issue on it
I have a hoverable dropdown inside a Navbar
When I move to a different page, the dropdown is still open
I have tried this in plain Bulma, the issue still remains
I am on Nuxt.js using SSR
I am using nuxt-link / equivalent of Vue router-link to navigate to a different page.
Here is the my default.vue file
<template>
<div class="ch-container">
<header class="ch-header">
<nav
class="navbar is-fixed-top"
role="navigation"
aria-label="main navigation"
>
<div class="navbar-brand">
<nuxt-link class="navbar-item" to="/">
<img
alt="CH Logo"
src="https://i.imgur.com/v35Kfc9.png"
width="28"
height="28"
/>
</nuxt-link>
<a
role="button"
class="navbar-burger burger"
aria-label="menu"
aria-expanded="false"
data-target="navbarBasicExample"
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<nuxt-link class="navbar-item" to="/news">
News
</nuxt-link>
<nuxt-link class="navbar-item" to="/resources">
Resources
</nuxt-link>
<nuxt-link class="navbar-item" to="/tickers">
Tickers
</nuxt-link>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">
More
</a>
<div class="navbar-dropdown">
<a class="navbar-item">
FAQ
</a>
<nuxt-link class="navbar-item" to="/contact">
Contact
</nuxt-link>
<hr class="navbar-divider" />
<a class="navbar-item">
Feature Request
</a>
</div>
</div>
</div>
<div class="navbar-end">
<div class="navbar-item">
<a href="#">
<fa :icon="faMoon" />
</a>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link is-arrowless">
<fa :icon="faExclamationCircle" />
</a>
<div class="navbar-dropdown">
<a class="navbar-item">
No new notifications
</a>
</div>
</div>
<div class="navbar-item">
<div class="buttons">
<nuxt-link class="button is-primary" to="/signup">
<strong>Sign up</strong>
</nuxt-link>
<nuxt-link id="login" class="button is-light" to="/login">
Log in
</nuxt-link>
</div>
</div>
</div>
</div>
</nav>
</header>
<main class="ch-main">
<nuxt />
</main>
<footer class="ch-footer is-hidden-mobile">
<div class="level">
<div class="level-left">
<div class="level-item">
<a href="#">
<span class="icon">
<fa :icon="faFacebookSquare" />
</span>
</a>
<a href="#">
<span class="icon">
<fa :icon="faTwitterSquare" />
</span>
</a>
<a href="#">
<span class="icon">
<fa :icon="faRedditSquare" />
</span>
</a>
</div>
</div>
<div class="level-right">
<div class="level-item">
©ch, All Rights Reserved
</div>
<div class="level-item">
<nuxt-link to="/contact">Contact</nuxt-link>
</div>
<div class="level-item">
<nuxt-link to="/terms-of-service">Terms</nuxt-link>
</div>
<div class="level-item">
<nuxt-link to="/privacy-policy">Privacy</nuxt-link>
</div>
</div>
</div>
</footer>
</div>
</template>
<script>
import {
faFacebookSquare,
faTwitterSquare,
faRedditSquare,
} from '#fortawesome/free-brands-svg-icons'
import { faMoon, faExclamationCircle } from '#fortawesome/free-solid-svg-icons'
export default {
computed: {
faFacebookSquare() {
return faFacebookSquare
},
faTwitterSquare() {
return faTwitterSquare
},
faRedditSquare() {
return faRedditSquare
},
faMoon() {
return faMoon
},
faExclamationCircle() {
return faExclamationCircle
},
},
mounted() {
// Get all "navbar-burger" elements
const $navbarBurgers = Array.prototype.slice.call(
document.querySelectorAll('.navbar-burger'),
0
)
// Check if there are any navbar burgers
if ($navbarBurgers.length > 0) {
// Add a click event on each of them
$navbarBurgers.forEach((el) => {
el.addEventListener('click', () => {
// Get the target from the "data-target" attribute
const target = el.dataset.target
const $target = document.getElementById(target)
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
el.classList.toggle('is-active')
$target.classList.toggle('is-active')
})
})
}
},
}
</script>
<style></style>
Here is a GIF illustrating the problem
How to close the dropdown after you move to a different page?
Ok, the whole thing makes it complicated, because the hover is triggered by css and therefore the dropdown can always be seen when the mouse is over it. You have to overwrite this state and solve it with vue events. We also have to put a watcher on the route to reset the state.
CodeSandbox - Example
<template>
<div class="container">
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<!-- <a class="navbar-item" href="https://bulma.io"> -->
<img src="https://bulma.io/images/bulma-logo.png" width="112" height="28">
<a
role="button"
class="navbar-burger burger"
aria-label="menu"
aria-expanded="false"
data-target="navbarBasicExample"
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item">Home</a>
<a class="navbar-item">Documentation</a>
<div
#mouseover="toggleDropdown(true)"
#mouseleave="toggleDropdown(false)"
class="navbar-item has-dropdown is-hoverable"
>
<a class="navbar-link">More</a>
<div class="navbar-dropdown" :style="{display: showDropdown ? 'block' : 'none' }">
<nuxt-link class="navbar-item" to="/about">About</nuxt-link>
<nuxt-link class="navbar-item" to="/jobs">Jobs</nuxt-link>
</div>
</div>
</div>
<div class="navbar-end">
<div class="navbar-item">
<div class="buttons">
<a class="button is-primary">
<strong>Sign up</strong>
</a>
<a class="button is-light">Log in</a>
</div>
</div>
</div>
</div>
</nav>
<Nuxt/>
</div>
</template>
<script>
export default {
data() {
return {
routeChange: false,
showDropdown: false
};
},
watch: {
$route() {
this.routeChange = true;
this.showDropdown = false;
}
},
methods: {
toggleDropdown(payload) {
if (this.showDropdown !== payload) this.routeChange = false;
if (!this.routeChange) {
this.showDropdown = payload;
}
}
}
};
</script>
This worked form me Bootstrap 5, in touch devices it uses click, on devices with mouse it uses hover
<template>
<span
v-if="item"
class="primary-navigation-list-dropdown"
#mouseover="isTouchscreenDevice ? null : openDropdownMenu()"
#mouseleave="isTouchscreenDevice ? null : closeDropdownMenu()"
>
<nuxt-link
to="#"
#click.prevent.native="openDropdownMenu"
v-click-outside="closeDropdownMenu"
:title="item.title"
:class="[
item.cssClasses,
{ show: isDropdownMenuVisible }
]"
:id="`navbarDropdownMenuLink-${item.id}`"
:aria-expanded="[isDropdownMenuVisible ? true : false]"
class="
primary-navigation-list-dropdown__toggle
nav-link
dropdown-toggle"
aria-current="page"
role="button"
data-toggle="dropdown"
>
{{ item.label }}
</nuxt-link>
<ul
:class="{ show: isDropdownMenuVisible }"
:aria-labelledby="`navbarDropdownMenuLink-${item.id}`"
class="
primary-navigation-list-dropdown__menu
dropdown-menu-list
dropdown-menu"
>
<li
v-for="item in item.children" :key="item.id"
class="dropdown-menu-list__item"
>
<NavLink
:attributes="item"
class="dropdown-menu-list__link dropdown-item"
/>
</li>
</ul>
</span>
</template>
<script>
import NavLink from '#/components/Navigation/NavLink';
export default {
name: "DropdownMenu",
props: {
item: {
type: Object,
required: true,
},
},
data() {
return {
isDropdownMenuVisible: false,
isTouchscreenDevice: false
};
},
mounted() {
this.detectTouchscreenDevice();
},
methods: {
openDropdownMenu() {
if (this.isTouchscreenDevice) {
this.isDropdownMenuVisible = !this.isDropdownMenuVisible;
} else {
this.isDropdownMenuVisible = true;
}
},
closeDropdownMenu() {
if (this.isTouchscreenDevice) {
this.isDropdownMenuVisible = false;
} else {
this.isDropdownMenuVisible = false;
}
},
detectTouchscreenDevice() {
if (window.PointerEvent && ('maxTouchPoints' in navigator)) {
if (navigator.maxTouchPoints > 0) {
this.isTouchscreenDevice = true;
}
} else {
if (window.matchMedia && window.matchMedia("(any-pointer:coarse)").matches) {
this.isTouchscreenDevice = true;
} else if (window.TouchEvent || ('ontouchstart' in window)) {
this.isTouchscreenDevice = true;
}
}
return this.isTouchscreenDevice;
}
},
components: {
NavLink
}
};
</script>
<style scoped lang="scss">
.primary-navigation-list-dropdown {
&__toggle {
color: $white;
&:hover {
color: $blue;
}
}
&__menu {
margin-top: 0;
}
&__dropdown {
}
}
.dropdown-menu-list {
&__item {
}
&__link {
&.active,
&.nuxt-link-exact-active {
border-bottom: 1px solid $blue;
}
}
}
</style>
NavLink.vue
<template>
<component
:is="attributes"
v-bind="linkAttributes(attributes.path)"
:title="attributes.title"
:class="[ attributes.cssClasses ]"
class="nav-link active_"
aria-current="page"
prefetch
>
{{ attributes.label }}
</component>
</template>
<script>
export default {
name: 'NavLink',
props: {
attributes: {
type: Object,
required: true
}
},
methods: {
linkAttributes(path) {
if (path.match(/^(http(s)?|ftp):\/\//) || path.target === '_blank') {
return {
is: 'a',
href: path,
target: '_blank',
rel: 'noopener'
}
}
return {
is: 'nuxt-link',
to: path
}
}
}
}
</script>
click-outside.js
import Vue from 'vue'
Vue.directive('click-outside', {
bind: function (el, binding, vnode) {
el.clickOutsideEvent = function (event) {
if (!(el == event.target || el.contains(event.target))) {
vnode.context[binding.expression](event);
}
};
document.body.addEventListener('click', el.clickOutsideEvent)
},
unbind: function (el) {
document.body.removeEventListener('click', el.clickOutsideEvent)
},
});
I’m not using Vue, but using Meteor (where similar route-calling trips up the Bulma drop-down removal).
Slightly hacky way to fix it, but the dropdown is made visible because the .has-dropdown element has the .is-hoverable class. So to fix, on any click on the dropdown’s items, I run this:
// Remove the hover effect = dropdown disappears
$(".has-dropdown").removeClass("is-hoverable");
// Tiny time later, put it back, so hover-drop works anew
setTimeout(function() {
$(".has-dropdown").addClass("is-hoverable");
}, 100);
Doesn’t really matter that it would hit all the dropdowns if you have more than one, because resetting them all is harmless when routing to a new page. But if you’re fussy you could target just the closest dropdown.
As Bulma’s mobile view doesn’t activate an on-hover effect anyway, this doesn’t break in the mobile “burger menu” version of my navbar.
Works OK for my project on Chrome, Safari, Firefox.