I am trying to call the showMenu method on the click event based on the value passed on "open" variable.
But I m getting cannot read properties of undefined while, open is a defined variable.
I have tried to console log something on the click events it works, but whenever I try to interact with the open variable which is defined to the best of my knowledge I am getting the undefined error message.
<template>
<nav>
<div class="header-one">
<div class="header">
<div class="logo">Logo</div>
<div class="navigation" id="nav">
<nuxt-link to="/">Home</nuxt-link>
<nuxt-link to="/">About</nuxt-link>
<nuxt-link to="/">Login</nuxt-link>
<nuxt-link to="/">Logout</nuxt-link>
<nuxt-link to="/">Profile</nuxt-link>
</div>
**<div class="burger" id="burger" #click="showMenu">**
<div></div>
<div></div>
<div></div>
</div>
</div>
</div>
<MobileMenu v-if="this.open"/>
</nav>
</template>
<script>
import MobileMenu from "~/components/MobileMenu";
export default {
name: "HeaderMenu",
MobileMenu,
data: function(){
return{
**open: false,**
}
},
methods:{
showMenu:() =>{
**this.open = !this.open**
}
}
}
</script>
This happen because your showMenu function is an arrow function wich change the scope of this.
Here this refer to the scope of the function instead of the components
Use a classic function instead
Here is an example
new Vue({
el: '#app',
data: () => {
return {
open: false
}
},
methods: {
toggleOpen1(){
this.open = !this.open
},
toggleOpen2: () => {
this.open = !this.open
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>Open : {{ open }}</div>
<button #click="toggleOpen1">Toggle open 1</button>
<button #click="toggleOpen2">Toggle open 2</button>
</div>
As you can see, the toggleOpen1 (classic function) methods work whereas the toggleOpen2 (arrow function), doesn't work.
Related
I'm starting with vue and I'm making a test to change a value when a button is clicked , but is not working
<template>
<div class="row">
{{ show }}
<div class="col-md-4">
<button class="btn btn-primary" #click="change_show">Good</button>
</div>
<div class="col-md-4">
<button class="btn btn-primary">Bad</button>
</div>
<div class="col-md-4">
<button class="btn btn-primary">Food</button>
</div>
</div>
</template>
<script>
export default {
name: "buttons",
data(){
return{
show: true
}
},
methods:{
change_show(event){
show = !show;
}
}
}
</script>
<style scoped>
</style>
I get this error
Uncaught ReferenceError: show is not defined
How I can access to the variables and change it?
You have declared your variable incorrectly, you should add show (and all your reactive variables) in the return:
data(){
return{
show: true
};
},
Assuming I have a component named Modal which I want to reuse across my application and in the meanwhile I also want to dynamically bind functions to its Yes button, how can I pass a function to the #click event of the Yes button in my Modal as a prop. For instance:
//data tags are used for fast markup here only
<tamplate>
<div>
<div :id="`${id}`" data-modal>
<div data-modal-title>
{{title}}
</div>
<div data-modal-body>
{{body}}
</div>
<div data-modal-footer>
<button #click="//event to be passed//" data-modal-button-ok>Yes</button>
<button data-modal-button-cancel>Yes</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'modal',
props: [
'id',
'title',
'body',
// event?
]
}
</script>
and when using this modal, how should the event be passed?
<Modal id="x" title="y" body="x" //event="????"//></Modal>
You should $emit an event (name, say, yes) at the modal:
<button #click="$emit('yes')" data-modal-button-ok>Yes</button>
Or, use a method:
<button #click="handleYesClick" data-modal-button-ok>Yes</button>
methods: {
handleYesClick() {
this.$emit('yes');
}
}
And listen to it in the parent using:
<modal ... v-on:yes="someCodeToExecute"></modal>
or (shorthand):
<modal ... #yes="someCodeToExecute"></modal>
Demo:
Vue.component('modal', {
template: "#modal",
name: 'modal',
props: ['id', 'title', 'body']
})
new Vue({
el: '#app',
data: {},
methods: {
methodAtParentYes() {
alert('methodAtParentYes!');
},
methodAtParentCancel() {
alert('methodAtParentCancel!');
}
}
})
<script src="https://unpkg.com/vue"></script>
<template id="modal">
<div>
<div :id="`${id}`" data-modal>
<div data-modal-title>
{{title}}
</div>
<div data-modal-body>
{{body}}
</div>
<div data-modal-footer>
<button #click="$emit('yes')" data-modal-button-ok>Yes</button>
<button #click="$emit('cancel')" data-modal-button-cancel>Cancel</button>
</div>
</div>
</div>
</template>
<div id="app">
<modal id="1" title="My Title" body="Body" #yes="methodAtParentYes" #cancel="methodAtParentCancel"></modal>
</div>
There are two ways to do this.
The first way is you could pass the method down as a prop, just like you would pass anything else and then in the Modal component just call that prop right in the click handler.
Parent.vue
<template>
<Modal id="x" title="y" body="x" :handleYes="handleYes"></Modal>
</template>
<script>
methods: {
handleYes () {
// do something
}
}
</script>
Modal.vue
<button #click="handleYes()">Yes</button>
The other way is to use $emit. So in Modal.vue you would define a method to emit an event and then listen for that event in the parent and do call the method there.
Modal.vue
<template>
<button #click="emitEvent">Yes</button>
</template>
<script>
methods: {
emitEvent () {
this.$emit('userClickedYes')
}
}
</script>
Parent.vue
<template>
<Modal id="x" title="y" body="x" #userClickedYes="handleYes"></Modal>
</template>
<script>
methods: {
handleYes () {
// do something
}
}
</script>
I am developing an application and I am using Vue 2 as my javascript framework, I tried to declare some components and use them in my html pages
this is my html:
<!DOCTYPE html>
<html>
<head>
<title></title>
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.1/css/bulma.css" />
</head>
<body>
<div id="modal_element" >
<modal v-if="showModal" ></modal>
<button #click="showModal = true" >Show Modal</button>
</div>
<div id="root">
<ul>
<li v-for="task in incompeletedTasks" >
{{ task.description }}
</li>
</ul>
</div>
</body>
<script src="https://unpkg.com/vue#2.1.10/dist/vue.js" ></script>
<script src="main.js"></script>
<script src="modal.js" ></script>
<script>
let main_data = {
tasks : [
{ description : "Go to the store ", completed : true },
{ description : "Leave the store" , completed : false }
]
}
new Vue({
el : "#root",
data : main_data,
computed : {
incompeletedTasks() {
return this.tasks.filter(task => !task.completed);
}
}
});
and this the modal.js file:
Vue.component('modal',{
template : '<div id="modal_element">
<div class="modal is-active">
<div class="modal-background"></div>
<div class="modal-content box">
<p>
Some Modal Text here ...
</p>
</div>
<button class="modal-close" #click="showModal = false" >
</button>
</div>',
data : function(){
return {
showModal : false
};
}
});
new Vue({
el : '#modal_element',
});
but the modal is not displayed, and I am getting the following error in the chrome console
[Vue warn]: Property or method "showModal" is not defined on the instance
but referenced during render. Make sure to declare reactive data
properties in the data option.
Question:
what modification do I have to make to get the code working? and html page successfully displays modal?
I think there are a couple of things.
You are creating 2 vue instances in this example (#root and #modal-element), so the data will not be able to be shared unless you have some store. Much better to have just a single instance and put components in that.
You will need to pass the component into the vue instance in order for it to be aware of the component.
Here is an example with alot of the stuff trimmed out.
https://jsfiddle.net/Austio/vhgztp59/2/
The gist of it is
var component = ...createComponentStuff
new Vue({
...otherVueStuff,
components: [component]
})
I'm trying to create an accordion using vuejs.
I found some examples online, but what I want is different. For SEO purpose I use "is" and "inline-template", so the accordion is kind of static not fully created in Vuejs.
I have 2 problems/questions:
1) I need to add a class "is-active" on the component based on user interaction(clicks), because of this I receive the following error.
Property or method "contentVisible" is not defined on the instance but
referenced during render. Make sure to declare reactive data
properties in the data option.
This probable because I need to set it at instance level. But "contentVisible" have a value (true or false) different for each component.
So I thought using at instance level an array of "contentVisible" and a props (pass thru instance) and custom events on child to update the instance values.
2) Could work but it is a static array. How can I make a dynamic array (not knowing the number of item components) ?
<div class="accordion">
<div>
<div class="accordion-item" is="item" inline-template :class="{ 'is-active': contentVisible}" >
<div>
<a #click="toggle" class="accordion-title"> Title A1</a>
<div v-show="contentVisible" class="accordion-content">albatros</div>
</div>
</div>
<div class="accordion-item" is="item" inline-template :class="{ 'is-active': contentVisible}" >
<div>
<a #click="toggle" class="accordion-title"> Title A2</a>
<div v-show="contentVisible" class="accordion-content">lorem ipsum</div>
</div>
</div>
</div>
var item = {
data: function() {
return {
contentVisible: true
}
},
methods: {
toggle: function(){
this.contentVisible = !this.contentVisible
}
}
}
new Vue({
el:'.accordion',
components: {
'item': item
}
})
Update
I create the following code but the custom event to send the modification from component to instance is not working, tabsactive is not changing
var item = {
props: ['active'],
data: function() {
return {
contentVisible: false
}
},
methods: {
toggle: function(index){
this.contentVisible = !this.contentVisible;
this.active[index] = this.contentVisible;
**this.$emit('tabisactive', this.active);**
console.log(this.active);
}
}
}
new Vue({
el:'.accordion',
data: {
tabsactive: [false, false]
},
components: {
'item': item
}
})
<div class="accordion" **#tabisactive="tabsactive = $event"**>
<div class="accordion-item" is="item" inline-template :active="tabsactive" :class="{'is-active': tabsactive[0]}">
<div>
<a #click="toggle(0)" class="accordion-title"> Title A1</a>
<div v-show="contentVisible" class="accordion-content">albatros</div>
</div>
</div>
<div class="accordion-item" is="item" inline-template :active="tabsactive" :class="{'is-active': tabsactive[1]}">
<div>
<a #click="toggle(1)" class="accordion-title" > Title A2</a>
<div v-show="contentVisible" class="accordion-content">lorem ipsum</div>
</div>
</div>
</div>
This works for me:
<template>
<div>
<ul>
<li v-for="index in list" :key="index._id">
<button #click="contentVisible === index._id ? contentVisible = false : contentVisible = index._id">{{ index.title }}</button>
<p v-if='contentVisible === index._id'>{{ index.item }}</p>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "sameName",
data() {
return {
contentVisible: false,
list: [
{
_id: id1,
title: title1,
item: item1
},
{
_id: id2,
title: title2,
item: item2
}
]
};
},
};
</script>
On point 1:
You have to define contentVisible as a vue instance variable, as you have accessed it with vue directive v-show, it searches this in vue data, watchers, methods, etc, and if it does not find any reference, it throws this error.
As your accordion element is associated with the parent component, you may have to add contentVisible data there, like following:
new Vue({
el:'.accordion',
data: {
contentVisible: true
}
components: {
'item': item
}
})
If you have multiple items, you may use some other technique to show one of them, like have a data variable visibleItemIndex which can change from 1 to n-1, where n is number of items.
In that case, you will have v-show="visibleItemIndex == currentIndex" in the HTML.
You can as well have hash for saving which index are to de displayed and which to be collapsed.
On point 2:
You can use v-for if you have dynamic arrays. you can see the documentation here.
I'm having a real hard time understanding what exactly it is you want or why you would want it, but I think this does it?
Vue.component('accordion-item', {
template: '#accordion-item',
methods: {
toggle() {
if(this.contentVisible){
return
}
if(this.$parent.activeTab.length >= 2){
this.$parent.activeTab.shift()
}
this.$parent.activeTab.push(this)
}
},
computed: {
contentVisible() {
return this.$parent.activeTab.some(c => c === this)
}
}
})
const Accordion = Vue.extend({
data() {
return {
activeTab: []
}
},
methods: {
handleToggle($event) {
this.activeTab = []
}
}
})
document.querySelectorAll('.accordion').forEach(el => new Accordion().$mount(el))
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<template id="accordion-item">
<div class="accordion-item" :class="{ 'is-active': contentVisible}">
<slot name="title"></slot>
<div v-show="contentVisible" class="accordion-content" #click="$emit('toggle', $event)">
<slot name="content"></slot>
</div>
</div>
</template>
<div class="accordion">
<accordion-item #toggle="handleToggle">
<p slot="title">a title</p>
<p slot="content">there are words here</p>
</accordion-item>
<accordion-item #toggle="handleToggle">
<p slot="title">titles are for clicking</p>
<p slot="content">you can also click on the words</p>
</accordion-item>
<accordion-item #toggle="handleToggle">
<p slot="title">and another</p>
<p slot="content">only two open at a time!</p>
</accordion-item>
<accordion-item #toggle="handleToggle">
<p slot="title">and #4</p>
<p slot="content">amazing</p>
</accordion-item>
</div>
so I am new to vue, but getting up to speed. However, I cannot make the props thing to work - it always remains undefined in my child component.
The idea of the below is to create a app-wide notification modal window to display notifications.
This is my app.js
require('./bootstrap');
import ModalNotification from './components/Modal-Notification.vue';
const app = new Vue({
el: '#app',
data : {
formInputs: {},
formErrors: {},
showModal: false
},
components: {ModalNotification}
});
This is my Modal-Notification.vue
<template>
<transition name="modal">
<div class="modal-mask" #click="$emit('close')">
<div class="modal-wrapper">
<div class="modal-container" #click.stop>
<!-- <div class="modal-header">
<slot name="header">
NOTIFICATION
</slot>
</div> -->
<div class="modal-body">
<slot name="body">
Bla bla
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
<button class="modal-default-button btn btn-success" #click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</template>
<script>
export default {
name: 'ModalNotification',
data: function() {
return {
};
},
props: ['showModal'],
mounted: function() {
console.log(this);
document.addEventListener("keydown", (e) => {
console.log(this.showModal);
if (this.showModal && e.keyCode == 27) {
this.$emit('close');
}
});
},
methods: {
}
}
</script>
And the relevant part of app.blade.php
<div class="container-fluid" id="app">
<button #click="showModal = true" class="btn btn-default">MODAL</button>
<modal-notification v-if="showModal" #close="showModal = false" :showModal="false">
<p slot="body" id="notification-message">hehe</p>
</modal-notification>
<div id="wrapper">
#yield('sidebar')
#yield('content')
</div>
</div>
I've tried everything out there, except switching to Browserify, babel and such stuff, but I don't think it should be needed - webpack should work just fine.
Please help, if you can.
You have mistake in the following snippet of code:
<modal-notification v-if="showModal" #close="showModal = false" :showModal="false">
<p slot="body" id="notification-message">hehe</p>
</modal-notification>
:showModal="false" is basically shortcut of v-bind:showModal="false", which tries to search vue instance variable in the value of attached HTML property(documentation). as you are passing false which is not a vue data variable, it is just passing null in showModal props.
If you want to pass only false, change the code to following:
<modal-notification v-if="showModal" #close="showModal = false" showModal="false">
<p slot="body" id="notification-message">hehe</p>
</modal-notification>
Edited
I think it is magic of camelCase-vs-kebab-case:
HTML attributes are case-insensitive, so when using non-string templates, camelCased prop names need to use their kebab-case (hyphen-delimited) equivalents:
You need to pass : show-modal="false"
<modal-notification v-if="showModal" #close="showModal = false" show-modal="false">
<p slot="body" id="notification-message">hehe</p>
</modal-notification>