Event and Data Handling Vue.js - javascript

I am new to Vue.js and I have trouble on answering my code activities:
So the menu button would open selections from the drop down box upon clicking, and when you click any of the selections, it would go back to the menu button
index.js
const vm = new Vue({
el: '#root',
data: () => {
{return {menuClick: true}}
},
methods: {
methodClick(e){
console.log(event.target.value)
}
},
template: `
<div #click="methodClick">
<a class='dropdown-button btn' href='#' v-if="methodClick">Open</a>
<ul class='dropdown-content'v-else="!methodClick">
<li>one</li>
<li>two</li>
<li class="divider"></li>
<li>three</li>
</ul>
</div>
`
});
index.html
<!--Don't edit this! Change to the index.js file (click on the link on the left side) and edit that file-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.min.css">
<style>.dropdown-content{ display: block !important; opacity: 1 !important; }</style>
<div id="root">
</div>
<!--Don't edit this! Change to the index.js file (click on the link on the left side) and edit that file-->
When I console log it, it just says it is undefined.

You haven't made use of the attribute 'menuClick'
const vm = new Vue({
el: '#root',
data: () => {
return {menuClick: false} // changes added
},
methods: {
methodClick(e){
console.log(event.target.value);
this.menuClick = !this.menuClick; // changes added
}
},
template: `
<div #click="methodClick">
<a class='dropdown-button btn' href='#' v-if="!menuClick">Open</a> // changes added
<ul class='dropdown-content' v-else> // changes added
<li>one</li>
<li>two</li>
<li class="divider"></li>
<li>three</li>
</ul>
</div>
`
});

Related

Get child element from element through an event with Vue

Goal
What I'm trying to achieve is that whenever the user clicks on the .menu-item element, the dropdown element .menu-item-dropdown will get a class that will show the element.
Problem
Currently, when I click on the .menu-item class element, the returned value from the console.log in showMobileNavDropdown() returns null. When clicking, the event.target element is .menu-item-main and not .menu-item. This happens only when I click on the text of the li. Otherwise this works as intended.
What would be the best way to include the li text so that I can still grab the .menu-item-dropdown class?
Vue
<template>
<div>
<nav class="nav">
<div class="nav-left">
<img class="logo" src="/images/logo.svg" alt="" />
</div>
<div class="nav-center">
<ul class="menu-items">
<li
class="menu-item"
#click="showMobileNavDropdown($event)"
>
<!-- Main Title On Nav -->
<a class="menu-item-main" href="#">Company</a>
<!-- Dropdown -->
<div class="menu-item-dropdown">
<ul>
<li class="">
About
</li>
<li class="">
Contact
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="nav-right">
Contact us
</div>
<div class="hamburger" #click="openMobileNav">
<span class="ham-line"></span>
<span class="ham-line ham-line-2"></span>
</div>
</nav>
<div class="mobile-nav"></div>
</div>
</template>
<script>
export default {
methods: {
openMobileNav() {
var mobileNav = document.querySelector(".mobile-nav");
mobileNav.classList.toggle("showMobileNav");
},
// This function
showMobileNavDropdown(event) {
console.log(event); // This element returns 'menu-item-main' and not 'menu-item'
console.log(event.target.querySelector(".menu-item-dropdown"));
},
},
};
</script>
Assuming you have a longer list of possible click-targets and can't or won't just use refs, I would use event-delegation and in the click-handler check, whether the clicked element is inside a clickable parent and based on that, toggle a class on the parent, which can then be used to select the child in CSS, for example to change it's opacity.
Vue.component('foo-bar', {
methods: {
toggleSubItemVisibility({ target }) {
const item = target.closest('.nav-item');
// we check because the user might have clicked on the surrounding ul
if (item) {
item.classList.toggle('active');
}
}
},
template: `
<nav>
<ul #click="toggleSubItemVisibility">
<li class="nav-item">
<span class="nav-label">Foo</span>
<div class="nav-sub-item">More details...</div>
</li>
<li class="nav-item">
<span class="nav-label">Bar</span>
<div class="nav-sub-item">More details...</div>
</li>
</ul>
</nav>`
})
new Vue({
el: '#app'
});
nav ul {
list-style-type: none;
display: flex;
flex-direction: row;
gap: 12px;
}
.nav-label {
cursor: pointer;
}
.nav-sub-item {
opacity: 0;
transition: opacity 1s ease;
}
.nav-item.active .nav-sub-item {
opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<foo-bar></foo-bar>
</div>
The solution was to either grab the element, and if that returned null, grab the sibling. This way, it's always possible to select the .menu-item-dropdown element. When clicking on the element, it can either be a child or sibling.
Part of Solution
var element =
event.target.querySelector(".menu-item-dropdown") ||
event.target.nextElementSibling;
element.classList.toggle("hide_drop");

Materialize tabs persist active tab on page refresh

I am building a single page application using vue.js with materialize and I have a navbar with tabs to navigate to different pages on my website. Materialize's tabs has an active property that displays which tab is currently selected. Every thing this far works perfectly.
This is materialize's tabs reference
The Issue
If you refresh the web page the tabs active property 'resets' to its default position. So I am trying to figure out how to persist the state of the navbar's active property and then reassign it to router-link.
My Code
<ul id="tabs" class="tabs tabs-transparent" :class="{ 'navbar--color': !changeNavColor }">
<li class="tab" id="home">
<router-link
id="home"
class="link"
:class="{ 'navbar--color': !changeNavColor }"
to="/"
>Home</router-link>
</li>
<li class="tab" id="services">
<router-link
id="services"
class="link"
:class="{ 'navbar--color': !changeNavColor }"
to="Services"
>Service</router-link>
</li>
<li class="tab" id="Preapproved">
<router-link
id="Preapproved"
class="link"
:class="{ 'navbar--color': !changeNavColor }"
to="PreApproved"
>Get Pre-Approved</router-link>
</li>
<li class="tab">
<router-link
id="cars"
class="link"
:class="{ 'navbar--color': !changeNavColor }"
to="Cars"
>inventory</router-link>
</li>
<li class="tab">
<router-link
id="testimonials"
class="link"
:class="{ 'navbar--color': !changeNavColor }"
to="Testimonials"
>Testimonals</router-link>
</li>
</ul>
if (localStorage.currentTab) {
this.currentTab = localStorage.currentTab;
var activeTab = document.getElementById(this.currentTab);
activeTab.classList.add("active");
console.log("saved");
}
$(document).ready(function() {
$("a").click(function(event) {
this.currentTab = event.target.id;
console.log(this.currentTab);
});
});
$(document).ready(function() {
$(".tabs").tabs();
});
window.addEventListener("scroll", this.onScroll);
},
beforeDestroy() {
window.removeEventListener("scroll", this.onScroll);
},
watch: {
currentTab(newTab) {
console.log("watch");
localStorage.currentTab = this.currentTab;
}
}
any help is appreciated
So I think you can use hash mechanism by using window.location.hash. You can put the data-toggle attribute in every tag anchor. And just check for location.hash !== ''
But the more Vue way would be through client side storage. Check the link its from vue docs. Although it is for the input field but you will get the idea of how to persist info by keeping a reference in client storage and on reload getting your desired state from there onward. Hope it helps.
Remove the indicator that is nested inside the first li, and the navigation works fine.
<ul id="tabs" class="tabs tabs-transparent">
<li id="tab" class="tab">
Home
<li class="indicator"></li> <!-- THIS RANDOM NESTED INDICATOR IS THE ISSUE -->
</li>
<li id="tab" class="tab">
Service
</li>
<li id="tab" class="tab">
Get Pre-Approved
</li>
<li id="tab" class="tab">
inventory
</li>
<li class="indicator" style="right: 488px; left: 0px;"></li> <!-- CORRECT INDICATOR, AUTOMATICALLY GENERATED BY MATERIALIZE -->
</ul>
This is how I ended up implementing this. First all of your router-links or <a> have to have unique id's
mounted() {
if (localStorage.currentTab) {
this.currentTab = localStorage.currentTab;
var element = document.querySelector(this.currentTab);
element.classList.add("active");
}
$(document).ready(function() {
$(".tabs").tabs();
$("a").click(function(event) {
this.currentTab = "#" + event.target.id;
localStorage.currentTab = this.currentTab;
});
});
},
watch: {
currentTab(newTab) {
localStorage.currentTab = this.currentTab;
}
}

Hiding a dropdown in Vuejs by clicking outside (component)

I'm creating a menu component on a Vuejs project. This menu has 2 dropdowns, I already created some methods and used Vue directives so when I click in 1 of them, the other hides and vice versa, I'd also like to know how to hide them by clicking outside.
I've tried 2 Vue libs but didn't work for me. Also, I'd like to do this manually if possible and not externally.
HTML:
<!-- menu -->
<div>
<ul>
<li><span>Home</span></li>
<li v-on:click="toggle1(), dropAreas =! dropAreas">
<span>Areas</span>
</li>
<li v-on:click="toggle2(), dropAdmin =! dropAdmin">
<span>Administration</span>
</li>
</ul>
</div>
<!-- /menu -->
<!-- dropdowns-->
<div v-if="dropAreas">
<ul>
<li>
<span>Kitchen</span>
</li>
<li>
<span>Baths</span>
</li>
</ul>
</div>
<div v-if="dropAdmin">
<ul>
<li>
<span>Profile</span>
</li>
<li>
<span>Services</span>
</li>
</ul>
</div>
<!-- /dropdowns-->
JS
data () {
return {
dropAreas: false,
dropAdmin: false
}
},
methods: {
toggle1 () {
if (this.dropAdmin === true) {
this.dropAdmin = false
}
},
toggle2 () {
if (this.dropAreas === true) {
this.dropAreas = false
}
}
}
*This code is being called in another component, "Home", like this:
<template>
<div>
<menu></menu>
<!-- [...] -->
</div>
</template>
Any ideas on how to do it manually? Is it possible? Thank you.
It's a bit of a hack, but you can do it using tabindex HTML attribute and :focus CSS pseudo-class:
new Vue({
el: '#app',
template: `
<div class="container">
<div
ref="menu"
id="menu"
tabindex="0"
>Menu</div>
<ul id="dropdown">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
`
});
#menu {
display: inline-block;
padding: 1em;
border: 1px solid #e6e6e6;
cursor: pointer;
}
#dropdown {
display: none;
}
#menu:focus + #dropdown {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app"></div>

vue way of .each()

I have a hard coded menu.vue file that basically gets the url slug and adds a class of active if it matches the <a> href. It works just fine but I am sure there is a cleaner way of doing it so I am not copy/pasting the same code over and over. In Jquery I know I could just loop through all the <a> in an each and see if slug matches the href and addClass
menu.vue
<template>
<div>
<!-- Main site menu -->
<nav>
<ul>
<li>about</li>
<li>faq</li>
<li>signup</li>
<li>login</li>
<li>Contact</li>
</ul>
</nav>
</div>
</template>
<script>
export default {
data(){
return {
slug: ''
}
},
created(){
var urlArray = window.location.pathname.split( '/' );
this.slug = urlArray[1];
},
}
</script>
Example: https://jsfiddle.net/wostex/63t082p2/46/
<div id="app">
<p v-for="link in links" :key="link">
<a :href="'/'+link" :class="{'active': slug == link}">{{link}}</a>
</p>
</div>
new Vue({
el: '#app',
data: {
links: [
'about',
'home',
'faq'
]
}
});
In this example each link has a corresponding class, e.g. 'home' class, 'faq' class.

How to change a session variable when clicking on `<a>` in Javascript

I can't find a way to make it works without using PHP and I don't want to use PHP. The thing is that I have a list with some options :
And I want to set a session variable when I click on one link.
I have tried to do <a onClick="displayAllContainers();">All containers</a> then in my JS I have tried to do
`function displayAllContainers() {
console.log("Display all");
}`
But it didn't work.
[EDIT] This fiddle doesn't work but I can put all the code without making a question that make 500lines so
import { Template } from 'meteor/templating';
import { Session } from 'meteor/session'
import { InfosContainers } from '/both/collections/infosContainers.js';
import { InfosMachines } from '/both/collections/infosMachines.js';
import './main.html';
import '../imports/ui/controlPanel.js';
import '../imports/ui/container.js';
import '../imports/ui/machine.js';
import '../imports/ui/chart.js';
import '../imports/ui/controlPanel.html';
import '../imports/ui/container.html';
import '../imports/ui/machine.html'
if (Meteor.isClient) {
//subscribe the publication of the server
Meteor.subscribe('infosContainers');
Meteor.subscribe('infosMachines');
}
function displayAllContainers() {
console.log("Display all");
}
Template.body.onRendered(function () {
});
Template.body.helpers({
//return all objects of the collection machines with the specified state
infosMachines() {
return InfosMachines.find({});
},
//return all objects of the collection containers with the specified state
infos(state) {
return InfosContainers.find({stateContainer: state});
},
//return all containers
infosCtn(){
return InfosContainers.find({});
},
//return true if we have >=1 container running. It's the emergency button
urgence() {
return InfosContainers.find({stateContainer: 'running'}).count() > 0;
}
});
<!-- form containers -->
<div class="col-md-6 col-sm-12 col-xs-12">
<div class="x_panel">
<div class="x_title">
<h2>Containers</h2>
<ul class="nav navbar-right panel_toolbox">
<li><a class="collapse-link"><i class="fa fa-chevron-up"></i></a>
</li>
<li class="dropdown">
<i class="fa fa-wrench"></i>
<ul class="dropdown-menu" role="menu">
<li><a onClick="displayAllContainers();return false;">All containers</a>
</li>
<li>Only running containers
</li>
<li>Only paused containers
</li>
<li>Only stopped containers
</li>
</ul>
</li>
<li><a class="close-link"><i class="fa fa-close"></i></a>
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="x_content">
<br/>
{{#each infosCtn}}
{{> container}}
{{/each}}
</div>
</div>
</div>
<!-- /form containers -->
You need to return false so as to prevent the default click behavior.
Just change this way:
<a onClick="displayAllContainers();return false;">All containers</a>
Try this,
HTML
<a onClick="displayAllContainers()" href="javascript:void(0);">All containers</a>
JS
function displayAllContainers() {
console.log("Display all");
}
EDIT
HTML
<a onClick="Meteor.call('displayAllContainers');
" href="javascript:void(0);">All containers</a>
JS
Meteor.methods({
'displayAllContainers': function(){
console.log("Hello world");
}
});
This works, JSFiddle
<script language="javascript">
function displayAllContainers(){
console.log("Display all");
}
</script>
<a onclick="displayAllContainers()">
All Containers
</a>
Write your function like below and it should work,
displayAllContainers = function(){
console.log("Display all");
}
I don't know why it works, I am putting a question for it, Will put link here. SO Question
Ok so thank you to everybody for you help because you have all helped me to found the solution and there is it :
1) I need to use template events in my JS
Template.body.events({
'click #anchorid': function (e) {
console.log("saucisse")
}
});
2) And I had to use the ID in the html but I can remove the onClick
<a id="anchorid">All containers</a>

Categories