How to emit event in Vue.js depending on the width of the screen - vue.js

I've got the following code from my sidebar. When any link is clicked, I'm also emitting an event to my root component and then invoking a function to hide the sidebar. This is working well but now I would like the event to be emitted only when the width of the screen is less than 768px so that I can emit the event only when I want to invoke the function.
<div class="side-links">
<ul>
<li>
<router-link #click="$emit('navLinkClicked')" :to="{ name: 'Home' }"
>Home</router-link
>
</li>
<li>
<router-link #click="$emit('navLinkClicked')" :to="{ name: 'Blog' }"
>Blog</router-link
>
</li>
<li>
<router-link #click="$emit('navLinkClicked')" :to="{ name: 'About' }"
>About</router-link
>
</li>
</ul>
</div>

You can use window.innerWidth to check the width of the current viewport. Global scope variables however is not available in the template, because its scoped to your component. You can either create a handler method:
<script>
methods: {
onNavLinkClicked() {
if (window.innerWidth < 768) {
this.$emit('navLinkClicked')
}
}
}
</script>
<template>
<div class="side-links">
<ul>
<li>
<router-link
#click="onNavLinkClicked"
:to="{ name: 'Home' }"
>
Home
</router-link
</li>
</ul>
</div>
</template>
or create a computed variable that defines if the width is below the treshold:
<script>
computed: {
isMobile() {
return window.innerWidth < 768;
}
}
</script>
<template>
<div class="side-links">
<ul>
<li>
<router-link
#click="isMobile ? $emit('navLinkClicked') : null"
:to="{ name: 'Home' }"
>
Home
</router-link
</li>
</ul>
</div>
</template>
Personally i'd go with the first option, so you'll only have to do the check once.
In Vue 2 it is also recommended to use kebab-case for event names.

Related

Scroll to section from child component in Vue.js

I am trying to understand how to emit an event from a child component to the parent that scrolls to the correct section via a ref or id. So for example the navigation menu is a child component with links. when I click a link, the corresponding section in the parent scrolls into view. I am coming from React so I am a bit thrown off. How do I pass parameters up to scroll to correct ref or id? basically the way anchorlinks act.
Here is what I got so far.. I am not sure I should be using links , I might need buttons since i'm not navigating pages.
Navigation.vue (Child)
<template>
<div class="navigation">
<img class="logo" alt="Vue logo" src="../assets/logo.png" />
<nav>
<ul>
<li>
<a #click="goSection" href="/">Contact</a>
</li>
<li>
Projects
</li>
<li>
Skills
</li>
<li>
Education
</li>
</ul>
</nav>
</div>
</template>
<script>
export default {
name: "Navigation",
//I want to emit an event in the parent where the page section scrolls in to view//
methods: {
goSection() {
this.$emit("go-section", this.value);
},
},
};
</script>
App.vue (Parent)
<template>
<div id="app">
<Navigation #go-section="goToSection" />
<section class="section" ref="projects">
<p>Projects</p>
</section>
<section class="section" ref="skills">
<p>Skills</p>
</section>
<section class="section" ref="contact">
<p>Contacts</p>
</section>
</div>
</template>
<script>
import Navigation from "./components/Navigation.vue";
export default {
name: "App",
components: {
Navigation,
},
methods: {
goToSection(event, value) {
var element = this.$refs[(event, value)];
var top = element.offsetTop;
window.scrollTo(0, top);
},
},
};
</script>
You can use a tag but prevent default link behavior, and pass ref to your goSection method :
Vue.component('navigation', {
template: `
<div class="navigation">
<img class="logo" alt="Vue logo" src="../assets/logo.png" />
<nav>
<ul>
<li>
<a #click.prevent="goSection('contact')" href="/">Contact</a>
</li>
<li>
Projects
</li>
<li>
Skills
</li>
<li>
Education
</li>
</ul>
</nav>
</div>
`,
methods: {
goSection(val) {
this.$emit("go-section", val);
},
},
})
new Vue({
el: '#demo',
methods: {
goToSection(value) {
const top = this.$refs[(value)].offsetTop;
window.scrollTo({
top: top,
left: 0,
behavior: 'smooth'
});
},
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<navigation #go-section="goToSection"></navigation>
<section class="section" ref="projects">
<p>Projects</p>
</section>
<section class="section" ref="skills">
<p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p>
</section>
<section class="section" ref="contact">
<p>Contacts</p>
</section>
</div>
You can emit an event with parameter (section ref for example) in the child component:
<a #click="$emit('go-section', 'contact')" href="/">Contact</a>
<a #click="$emit('go-section', 'projects')" href="/">Projects</a>
And then use this parameter in the parent's listener method
// template
<Navigation #go-section="goToSection" />
// js
goToSection(sectionRef) {
var element = this.$refs[sectionRef];
var top = element.offsetTop;
window.scrollTo(0, top);
},

How to make vuejs navbar link active on click without route

I have a vue bootstrap navbar. All of the nav items have a route except one. The one that doesn't (Contact) launches a modal on click. When an item is selected I want the item to be white.
I do this and it works for items that have a corresponding route:
:active='$route.name ==""'
Navbar
<ul class="navbar-nav">
<li class="nav-item">
<router-link class="nav-link" :to="{
path: '/',
hash: 'home',
}" :active='$route.name =="home"'>Home</router-link>
</li>
<li class="nav-item">
<router-link to="/About" :active='$route.name =="about"' class="nav-link">About</router-link>
</li>
<li class="nav-item">
<a class="nav-link" v-b-modal.modal-contact>Contact</a>
</li>
</ul>
How do I get the Contact item to be active on click?
I had the same problem as you and solved it like this.
First, you define a method that will use the name property of this.$route
export default {
name: 'App',
methods: {
getActiveNavLink(name) {
//This is for the navbar classes, you can modify them as
//as you need. (This will be assigned every-time we call this
//function).
let classString = "nav-item nav-link "
//We compare the given name with the route current name.
if (this.$route.name === name) {
//If it is true, we append to the class string the "active" value
classString += "active"
}
//Return the class string.
return classString;
}
}
}
Then in your navbar you do something like this
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="#">The navegation bar</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<router-link
:to="{ name: 'home'}"
:class="getActiveNavLink('home')" >
Home
</router-link>
<router-link
:to="{ name: 'secondRoute'}"
:class="getActiveNavLink('secondRoute')"><!--class="nav-item nav-link">-->
To the second route!
</router-link>
<router-link :to=...>
...
</router-link>
</div>
</div>
</div>
</nav>
That way, everytime you call the getActiveNavLink you pass the name of the route to compare, and if the name of the route is the same as your $route.name you will get an active navbar element!
Also, dont worry about the router-link classes being overwritten, they are not, the classes that we add, are appended, not overwritten.
its works for me for add class attributte and methods calling
You could use router.push(). When you open the modal, push to that path, when you leave the modal, push to the previous path.
https://router.vuejs.org/guide/essentials/navigation.html

Show Div When Hover Based on ID in Vue JS

I have a div that when i want hover to it, it will show other div. However, my first div is dynamic, it has an ID. So how will i able to hover to ID based on its ID?
It should be #mouseenter="hoverService{{services.id}} = true" but it causes error. So i made the code below to just static.
Here's my code below:
<template>
<div
class="col-md-3"
v-for="(services, index) in servicesFiltered"
:key="index"
#mouseenter="hoverService = true"
#mouseleave="hoverService = false"
>
<div class="service_background" v-if="hoverService">
<div class="mb-1" v-for="(sub_services, index) in services.menu_items" :key="index">
<router-link
:to="{ path: `/${sub_services.data}`}"
>
<a
href="#"
class="btn btn-outline-primary w-100 services_button"
>{{sub_services.text }}</a>
</router-link>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
hoverService: false
};
}
};
</script>
Try this code
https://codesandbox.io/s/y7p9qyyovz
You need to maintain hover for each item you can not manipulate using single variable for multiple items.

Is possible to pass data from slot-component to parent in vueJS?

I'm new in VueJS and I'm try to do something like this
I have 3 components, siderbar ; siderbar-item and sidebar-drop
siderbar.vue is a wrapper when you can put siderbar-item and sidebar-drop, and inside sidebar-drop you can put sidebar-item too
for example:
<sidebar>
<sidebar-item url="myurl" title="Dashboard" icon="fas fa-tachometer-alt" badge=""></sidebar-item>
<sidebar-drop title="News" icon="fas fa-tachometer-alt">
<sidebar-item url="myurl_news" title="News list" icon="fas fa-tachometer-alt" badge=""></sidebar-item>
<sidebar-item url="myurl_news_add" title="Add News" icon="fas fa-tachometer-alt" badge=""></sidebar-item>
</sidebar-drop>
<sidebar-drop title="Categories" icon="fas fa-tachometer-alt">
<sidebar-item url="myurl_categories" title="Categories List" icon="fas fa-tachometer-alt" badge=""></sidebar-item>
</sidebar-drop>
</sidebar>
sidebar.vue is very simple:
<aside>
<slot></slot>
</aside>
sidebar-item.vue
<template>
<ul class="menu-list" >
<li>
<a :href="url" :class="{'is-active': is_active }">
<span class="icon"><i v-if="icon" :class="icon"></i></span>
{{ title }}
<span v-if="badge" class="button badge is-info is-small is-pulled-right "> {{ badge }}</span>
</a>
</li>
</ul>
</template>
<script>
export default {
props: ['title', 'url', 'icon', 'badge'],
data() {
return {
is_active: this.$current_url === this.url
}
}
}
</script>
and sidebar-drop.vue
<template>
<ul class="menu-list" v-if="title">
<li #click="openMenu" :class="{'is-open': is_open}">
<a class="">
<span v-if="icon" class="icon"><i class="fas fa-tachometer-alt"></i></span>
{{ title }}
<span class="badge is-pulled-right" style=""> <i class="fas fa-chevron-left"></i> </span>
</a>
<slot></slot>
</li>
</ul>
</template>
<script>
export default {
props: ['title', 'icon'],
data() {
return {
is_open: false,
}
},
methods: {
openMenu(){
this.is_open = !this.is_open;
},
}
}
</script>
It works great, but I have a little problem. If you notice, sidebar-item has a data called "is_active", it only put a CSS class that this item is active. Great.
Now I'm trying to get into sidebar-drop if a least one of his children has data "is-active" in true, change the is_open sidebar-drop parent data to true.
Example: I click "News" item.
Ul li News is active... works!
Ul li parent drop is open... don't work
Is possible make that?
Thanks everyone!
Update:
I put it in Fiddler to more clear:
https://jsfiddle.net/h35qy2zs/
I do in sidebar-item a random is_active.
If any child of sidebar-drop (sidebar-item's) data is_active:true, sidebar-drop data is_open is true too.
So, it show in color yellow and open
Result:
But now, both are closed at refresh page.

Laravel Vuejs2 missing param for named route

I am having a hard time trying to isolate the console error.
My code is as follows:
myside.blade.php has:
<div class="nav-container">
<ul class="nav nav-icons justify-content-center" role="tablist">
<li class="nav-item">
<div>
<router-link class="nav-link text-primary" :to="{ name: 'property', params: { customerId: {{ $property_data[0]->listingId }} }}" #click.native="isVisible = !isVisible">
<i class="nc-icon nc-key-25"></i>
<br> Property
</router-link>
</div>
</li>
<li class="nav-item">
<router-link class="nav-link text-primary" :to="{ name: 'booking' }" #click.native="isVisible = !isVisible">
<i class="nc-icon nc-chart-pie-36"></i>
<br> Bookings
</router-link>
</li>
<li class="nav-item">
<a class="nav-link text-primary" href="" role="tab">
<i class="nc-icon nc-preferences-circle-rotate"></i>
<br> Performance
</a>
</li>
<li class="nav-item">
<a class="nav-link text-primary" href="" role="tab">
<i class="nc-icon nc-money-coins"></i>
<br> Revenue
</a>
</li>
<li class="nav-item">
<a class="nav-link text-primary" href="" role="tab">
<i class="nc-icon nc-layers-3"></i>
<br> Integration
</a>
</li>
</ul>
</div>
<property-page :customer-Id="{{ $property_data[0]->listingId }}" v-if="isVisible"></property-page>
<router-view></router-view>
My routes.js file:
import VueRouter from 'vue-router';
let routes = [
{
path: '/property/:customerId',
name: 'property',
component: require('./components/PropertyPage'),
props: true
},
{
path: '/booking',
name: 'booking',
component: require('./components/BookingPage')
}
];
export default new VueRouter({
routes,
linkActiveClass: 'nav-link text-success'
});
my app.js file:
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
import router from './routes';
import PropertyPage from './components/PropertyPage.vue';
import BookingPage from './components/BookingPage.vue';
new Vue({
el: '#root',
router,
data: {
NavPrimaryClass: 'nav-link text-primary',
NavClass: 'nav-link',
isVisible: true
},
components: {
'property-page': PropertyPage,
'booking-page': BookingPage
}
})
my PropertyPage.vue file:
<template>
<div>
<div class="ajax-loader">
<img src="/loader/ajax-loader.gif" width="300px" v-if="loading" />
</div>
<div v-if="propertyDataCheck">
</div>
</div>
</template>
<script>
import moment from 'moment';
import axios from 'axios';
export default {
props: {
customerId: {
type: Integer,
required: true,
default: 0
}
},
data() {
return {
propertyData: [],
loading: false
}
},
computed: {
propertyDataCheck () {
return this.propertyData.length;
}
},
mounted() {
this.loading = true;
axios.get('/ajax/propertydata/' + this.customerId)
.then(function(response) {
this.propertyData = response.data;
this.loading = false;
}.bind(this))
.catch(function() {
this.loading = false;
}.bind(this));
}
}
</script>
<style>
.ajax-loader {
position: absolute;
left: 40%;
top: 15%;
margin-left: -32px; /* -1 * image width / 2 */
margin-top: -32px; /* -1 * image height / 2 */
}
</style>
The end result, a console error which I have spent hours trying to work out where it is coming from.
Error:
[vue-router] missing param for named route "property": Expected "customer" to be defined
I needed a component to be loaded upon page load (outside of vue-router) which is why I have <property-page :customer-Id="{{ $property_data[0]->listingId }}" v-if="isVisible"></property-page> tags and is bound to a method which will switch upon a router-link click (in case you were wondering).
My understanding of this error is the variable for the first router-link which is generated from a Laravel model DB query {{ $property_data[0]->listingId }} should be checked within a v-if wrapped around the vue-router? I have also done this to no avail.
Any help would be much appreciated!
Believe it or not, you need to use strict kebab-case (all lower case!) html-properties in vue-templates. Change customer-Id to customer-id
<property-page :customer-Id=... // does not get mapped to this.customerId
<property-page :customer-id=... // does get mapped to this.customerId, yay!