Vue.js: Simple click function not firing - javascript

I have a very simple app:
<div id="show_vue">
<page-change page="bio" #click="changeThePage"></page-change>
<page-change page="health" #click="changeThePage"></page-change>
<page-change page="finance" #click="changeThePage"></page-change>
<page-change page="images" #click="changeThePage"></page-change>
</div>
Vue.component("page-change", {
template: "<button class='btn btn-success'>Button</button>",
props: ["page"]
})
var clients = new Vue({
el: '#show_vue',
data: {
currentRoute: window.location.href
},
methods: {
changeThePage: function() {
console.log("this is working")
}
}
})
...but when I click the <page-change></page-change> button, nothing is logged to the console. I know I'm missing something simple but I'm not getting any errors.
How do I make my click fire changeThePage

When you do :
<page-change page="bio" #click="changeThePage"></page-change>
That means that your are waiting page-change component emit the click event.
Best solution (thanks to #aeharding) : Use .native event modifier
<page-change page="bio" #click.native="changeThePage"></page-change>
Solution 1 : emit click event from child component :
Vue.component("page-change", {
template: "<button #click='clicked' class='btn btn-success'>Button</button>",
props: ["page"],
methods: {
clicked: function(event) {
this.$emit('click', this.page, event);
}
}
})
For information event is the default value passed by Vue for native event like click : DOM event
Solution 2 : emit directly from parent component :
Vue.component("page-change", {
template: "<button class='btn btn-success'>Button {{ page }}</button>",
props: ["page"]
})
var clients = new Vue({
el: '#show_vue',
data: {
currentRoute: window.location.href,
pages: [
'bio', 'health',
'finance', 'images'
]
},
methods: {
changeThePage: function(page, index) {
console.log("this is working. Page:", page, '. Index:', index)
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.5/vue.js"></script>
<div id="show_vue">
<span v-for="(page, index) in pages" :key="index+page"
#click="changeThePage(page, index)">
<page-change :page="page"></page-change>
</span>
</div>

The best way to do this is to use the .native event modifier.
For example:
<my-custom-component #click.native="login()">
Login
</my-custom-component>
Source: https://v2.vuejs.org/v2/guide/components.html#Binding-Native-Events-to-Components

apparently v-on:click seems to work better with the .native event modifier.
try page="bio" v-on:click.native="changeThePage()"></page-change>. It worked for me.

Related

VueJS: v-on:click not working on clicking the <button>

I have a Vue app configured by NuxtJS.
I have the following template and worker methods that should be called upon clicking the button. But they are not being called.
<template>
<button class="btn google-login" id="google-login" #click.native="googleLogin">
<img src="~assets/google-icon.svg" alt />
Login with Google
</button>
</template>
<script>
import firebase from "firebase";
export default {
data() {
return {
showPassword: false,
checkbox1: false
};
},
mounted: function() {
console.log(firebase.SDK_VERSION);
},
methods: {
googleLogin: function(event) {
console.log('Reached inside the function');
let googleProvider = new firebase.auth.GoogleAuthProvider();
googleProvider.addScope(
"https://www.googleapis.com/auth/contacts.readonly"
);
firebase.auth().useDeviceLanguage();
console.log(googleProvider);
}
}
};
</script>
I have the method inside the methods object. I have tried multiple solutions v-on:click, #click, #click.prevent but none seem to be working
.native event modifier is used with elements when you are trying listen any event happening in the child Component from the root Component.
For example you have a component button-counter, and your parent component need to listen to the click event happening in the button-counter component.
In your case you just neeed to trigger click event using #click="googleLogin"
Official Documentation
Read More
Sample implementation
new Vue({
el: "#app",
name: "MyApp",
components: {
'button-counter': {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
},
},
methods: {
googleLogin: function (event) {
console.log('Reached inside the function');
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.4/vue.js"></script>
<div id="app">
<button class="btn google-login" id="google-login" #click="googleLogin">
Login with Google
</button>
<button-counter #click.native="googleLogin" />
</div>
You need to trigger the click event this way.
#click="googleLogin"

child component emit an custom event, but parent component's listener not triggered

I'm registered a child component in Vue, the child will emit an custom event, but the parent component's listener not triggered.
<template id="add-item-template">
<div class="input-group">
<input #keyup.enter="addItem" v-model="newItem" >
<span class="input-group-btn">
<button #click="addItem" type="button">Add!</button>
</span>
</div>
</template>
<div id="app" class="container">
<h2>{{ title }}</h2>
<add-item-component v-on:itemAdd="addAItem"></add-item-component>
</div>
Vue.component('add-item-component', {
template: '#add-item-template',
data: function () {
return {
newItem: ''
};
},
methods: {
addItem() {
this.$emit('itemAdd');
console.log("itemAdd In add-item-component");
}
}
});
new Vue({
el: '#app',
data: {
title: 'Welcome to Vue'
},
methods: {
addAItem: function () {
console.log("In #app, addAItem()!");
}
}
});
The "itemAdd In add-item-component" log show in console, but "In #app, addAItem()!" log not, the #app's method addAItem not invoked.
The problem is that custom events should not be named with camelCase. If you look at the error message in the console, it tells you:
Event "itemadd" is emitted in component but the handler is registered for "itemAdd"
The component is emitting a lowercase version even though you've used camelCase to name it. Renaming to all lowercase or kebab-case will fix it.
In the parent template:
<add-item-component #itemadd="addAItem">
Emitting from the child:
this.$emit('itemadd');
This is discussed a bit with Evan You (Vue creator) here

Hook=componentUpdated of Vue directive not triggered

I just met one issue, if one component only update its own data, it will not trigger the hook=componentUpdated of the directive at the parent component.
As Vue official Guide said:
componentUpdated: called after the containing component’s VNode and
the VNodes of its children have updated.
It seems componentUpdated should be triggered.
Did I do something wrong? or misunderstand something?
At below demo, hit Click Me! button then you will see componentUpdated is not called.
But when click change data (execute similar behavior with click me!, the difference is it changes the data at parent component), it will trigger correctly.
Many thanks for any.
Vue.config.productionTip = false
Vue.component('child', {
template: `<div>{{point}}
<span style="background-color:gray;font-weight:bold;color:red">
-{{mytest}}
</span>
<button #click="plusOne()">Click me!</button>
</div>`,
props: ['point'],
data(){
return {
mytest: 1
}
},
updated: function () {
console.log('updated component=child')
},
methods: {
plusOne() {
this.mytest += 1
}
}
})
let vMyDirective = {}
vMyDirective.install = function install (Vue) {
Vue.directive('my-directive', {
inserted: function () {
console.log('!!!directive for inserted')
},
bind: function bind (el, binding, vnode) {
console.log('!!!directive for bind')
},
componentUpdated: function componentUpdated (el, binding, vnode) {
console.log('!!!directive for component updated')
},
update: function () {
console.log('!!!directive for update')
}
})
}
Vue.use(vMyDirective)
new Vue({
el: '#app',
data() {
return {
testValues: ['label a', 'label b'],
testIndex: 1
}
},
methods:{
pushArray: function() {
this.testValues.push('label c')
},
changeData: function () {
this.testIndex += 1
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<button v-on:click="pushArray()">Add one Child!!!</button>
<button v-on:click="changeData()">Change Data - {{testIndex}}</button>
<div v-my-directive>
<child v-for="(item, index) in testValues" :key="index" :point="item"></child>
</div>
</div>
Based on Vue Team Feedback, it is not one issue on the hook=componentUpdated, it is my misunderstanding on the words.
For the prerequisite of the hook=comopnentUpdated is triggered, it is the VNode which the directive binds to already changed. That means if only child VNode changes, Vue will not catch it probably like what #Jacob Goh said in the comments (only flows one way).
So componentUpdated doesn't means it will detect child components are updated or not, it only means when will be triggered.

VueJS2 how to dynamically emit event to parent component?

just like the title i need to dynamically emit an event to parent component's methods, i have a component structured like this
<TableComponent
:actionmenus="actionmenus"
#edit="parenteditmethod"
#delete="parentdeletemethod">
</TableComponent>
here is actionmenus object
actionmenus: [
{title: 'Edit', emitevent: 'edit'},
{title: 'Delete', emitevent: 'delete'}
]
and then here is snippet of my tablecomponent
...
<ul>
<li v-for="menu in actionmenus"><a #click="$emit(menu.emitevent)" class="text-menu">{{ menu.title }}</a></li>
</ul>
...
i know this should be easily done by $emit('edit') or $emit('delete') without using actionmenus object but the $emit() part should be dynamic based on the passed array actionmenus so that the tablecomponent can be re-used on different case. how should i approaching this? is there any way?
From what I understand, you would like to emit an event from the child component to the parent, and pass some data with the emit (sorry if thats not the case).
As you know, you can emit events in the child component like this :
$emit("EVENT");
And Catch it in the parent like this :
<childTag v-on:EVENT="parentFunction"></childTag>
You can also pass data to the parent from the child like this :
$emit("EVENT",DATA);
And catch the data in the parent function like this
<childTag v-on:EVENT="parentFunction"></childTag>
...
methods{
parentFunction(DATA){
//Handle the DATA object from the child
}
}
Hope this helps and best of luck!
#codincat is right, that it all works. It's true also for Vue3
const rootComponent = {
el: "#app",
data: function () {
return {
actionmenus: [
{ title: "Edit", emitevent: "edit" },
{ title: "Delete", emitevent: "delete" }
]
};
},
methods: {
parenteditmethod() {
console.log("edit");
},
parentdeletemethod() {
console.log("delete");
}
}
};
const app = Vue.createApp(rootComponent);
app.component("table-component", {
props: { actionmenus: Array },
template: `
<ul>
<li v-for="menu in actionmenus">
<a #click="$emit(menu.emitevent)" class="text-menu">{{ menu.title }}</a>
</li>
</ul>`
});
const rootComponentInstance = app.mount("#app");
<!-- https://stackoverflow.com/questions/43750969/vuejs2-how-to-dynamically-emit-event-to-parent-component -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.29/vue.global.min.js"></script>
<div id="app">
<table-component :actionmenus="actionmenus" #edit="parenteditmethod" #delete="parentdeletemethod">
</table-component>
</div>

Vue instance inside Vue instance

Sup people!
I got this HTML code here:
// index.html
<div data-init="component-one">
<...>
<div data-init="component-two">
<button #click="doSomething($event)">
</div>
</div>
This basically references a Vue instance inside another Vue instance if I understood everything correctly. The respective JS code is split up in two files and looks like this:
// componentOne.js
new Vue(
el: '[data-init="component-one"]',
data: {...},
methods: {...}
);
// componentTwo.js
new Vue(
el: '[data-init="component-two"]'
data: {...}
methods: {
doSomething: function(event) {...}
}
);
Now, the problem with this is, that doSomething from componentTwo never gets called.
But when I do some inline stuff, like {{ 3 + 3 }}, it gets computed like it should. So Vue knows there is something. And it also removes the #click element on page load.
I tried fiddling around with inline-template as well, but it doesn't really work as I'd expect it to in this situation. And I figured it isn't meant for this case anyway, so I dropped it again.
What would the correct approach be here? And how can I make this work the easiest way possible with how it's set up right now?
The Vue version we use is 2.1.8.
Cheers!
The problem is that you have two vue instances nested to each other.
If the elements are nested, then you should use the same instance or try components
https://jsfiddle.net/p16y2g16/1/
// componentTwo.js
var item = Vue.component('item',({
name:'item',
template:'<button #click="doSomething($event)">{{ message2 }</button>',
data: function(){
return{
message2: 'ddddddddddd!'
}},
methods: {
doSomething: function(event) {alert('s')}
}
}));
var app = new Vue({
el: '[data-init="component-one"]',
data: {
message: 'Hello Vue!'
}
});
<div data-init="component-one">
<button >{{ message }}</button>
<item></item>
</div>
Separate instances work if they are independant of each other.
as follows:
https://jsfiddle.net/p16y2g16/
var app = new Vue({
el: '[data-init="component-one"]',
data: {
message: 'Hello Vue!'
}
});
// componentTwo.js
var ddd = new Vue({
el: '[data-init="component-two"]',
data: {
message: 'ddddddddddd!'
},
methods: {
doSomething: function(event) {alert('s')}
}
});
But when I do some inline stuff, like {{ 3 + 3 }}, it gets computed like it should. So Vue knows there is something.
Because you have parent instance 'componentOne'. It activated Vue for this template. If you need to set another instance inside, you have to separate part of template. Example (it can lag in snippet!) .
Alternative
https://jsfiddle.net/qh8a8ebg/2/
// componentOne.js
new Vue({
el: '[data-init="component-one"]',
data: {
text: 'first'
},
methods: {}
});
// componentTwo.js
new Vue({
el: '[data-init="component-two"]',
data: {
text: 'second'
},
template: `<button #click="doSomething($event)">{{text}}</button>`,
methods: {
doSomething: function(event) {
console.log(event);
}
}
});
<script src="https://vuejs.org/js/vue.min.js"></script>
<div data-init="component-one">
{{text}}
</div>
<div data-init="component-two">
</div>
The button element inside component-two is referenced as a slot in Vue.
The evaluation of the #click directive value happens in the parent component (component-one, which host component-two). Therefor, you need to declare the click handler over there (over component-one).
If you want the handler to be handled inside component-two, you should declare a click directive for the slot element in it's (component-two) template, and pass the handler function, for instance, as a pop.
good luck.
You're doing everything right except you've nested the 2nd Vue instance inside the 1st. Just put it to the side and it will work as expected.
Vue ignores binding more than once to the same element to avoid infinite loops, which is the only reason it doesn't work nested.
Use vue-cli to create a webpack starter app. vue init app --webpack
Then, try to structure your components this way. Read more: https://v2.vuejs.org/v2/guide/components.html#What-are-Components
This is main.js
import Vue from 'vue'
import ComponentOne from './ComponentOne.vue'
import ComponentTwo from './ComponentTwo.vue'
new Vue({
el: '#app',
template: '<App/>',
components: {
ComponentOne,
ComponentTwo
}
})
This is ComponentOne.vue
<template>
<div class="user">
<div v-for="user in users">
<p>Username: {{ user.username }}</p>
</div>
</div>
</template>
<script>
export default {
data () {
return {
users: [
{username: 'Bryan'},
{username: 'Gwen'},
{username: 'Gabriel'}
]
}
}
}
</script>
This is ComponentTwo.vue
<template>
<div class="two">
Hello World
</div>
</template>
<script>
export default {
}
</script>
<div th:if="${msg.replyFloor}">
<div class="msg-lists-item-left">
<span class="msg-left-edit"
th:classappend=" ${msg.unreadCount == 0} ? 'msg-all-read' ">您在</span>
<span th:text="${msg.topic.title}"
class="msg-left-edit-res"
th:classappend=" ${msg.unreadCount == 0} ? 'msg-all-read' ">问题回答</span>
<span th:text="${msg.type.name}"
class="msg-left-edit "
th:classappend=" ${msg.unreadCount == 0} ? 'msg-all-read' ">帖子相关</span>
<span class="msg-left-edit-number" >
产生了<span th:text="${msg.unreadCount} ? : ${msg.unreadCount} + '条新' : ${msg.unreadCount} + '条' "
th:class="${msg.unreadCount} ? : 'number-inner':''">2132条</span>回复
</span>
</div>
<div class="msg-lists-item-right">
<span th:text="${msg.lastShowTime}">2017-8-10</span>
</div>
</div>

Categories