VueJs 2 emit custom event firing, but not being "heard" - javascript

Probably not possible, but I have an object that extends Vue/ VueComponent (tried both) that $emits a custom event that would normally be caught on its parent.
Please see this pen: https://codepen.io/anon/pen/MvmeQp?editors=0011 and watch the console.
class nonVueComponent extends Vue {
constructor(age,...args){
super(args)
console.log('new Blank Obj')
setTimeout(() => {
console.log('customEvent event does fire, but nothing hears it. Probably because it isnt in the DOM?', age)
this.$emit('customEvent', `custom event from nonVueComponent...${age}`)
},500)
}
}
Vue.component('test', {
template: `<div>
{{content}}
<child :childAge="age" #customEvent="customEvent"></child>
<child-secondary #secondaryEvent="customEvent"></child-secondary>
</div>`,
props: {},
data () {
return {
content: 'hello from component!',
age : 20
}
},
methods : {
customEvent(data){
console.log('PARENT: custom event triggered!', data)
this.content = data
},
secondaryEvent(data){
console.log('PARENT: !!secondary custom event triggered', data)
this.content = data
}
}
})
Vue.component('child',{
template: `<div>+- child {{childAge}}</div>`,
props: ['childAge'],
data () {
outsideOfVue: new nonVueComponent(this.childAge)
}
})
Vue.component('child-secondary',{
template: `<div>+- secondary event</div>`,
mounted(){
setTimeout( ()=>{
this.$emit('secondaryEvent', 'from secondary event....')
},125 )
}
})
let vm = new Vue({ el: '#app'})
Aside from using an eventBus, is there any other way to get the event up and out from the <child> ? Maybe make the nonVueComponent a mixin?
Thanks.

code:https://codepen.io/anon/pen/EvmmKa?editors=0011
The object who emits the event should be the instace of child-secondary.
Try to convey the instance to the nonVueComponent's constructor.
class nonVueComponent extends Vue {
constructor(age,comp,...args){
super(args)
console.log('new Blank Obj')
setTimeout(() => {
console.log('customEvent event does fire, but nothing hears it. Probably because it isnt in the DOM?', age)
comp.$emit('customEvent', `custom event from nonVueComponent...${age}`)
},500)
}
}

Related

Vue render button

My question is about render a button on vue instance, to click in a button and then it render another button with event click, If I simple mount the button it dont get the function tes.
const Hello = {
props: ['text'],
template: '<button v-on:click="tes"> </button> ',
};
new Vue({
el: '#app',
data: {
message: 'Click me'
},
methods:{
alertar: function(event){
const HelloCtor = Vue.extend(Hello);
var instance = new HelloCtor({
propsData: {
text: 'HI :)'
}
})
instance.$mount() // pass nothing
this.appendChild(instance.$el)
},
tes: function(){
alert('Teste');
}
}
})
Erro :
vue.js:597 [Vue warn]: Invalid handler for event "click": got undefined
(found in <Root>)
warn # vue.js:597
(index):52 Uncaught TypeError: this.appendChild is not a function
at Vue.alertar ((index):52)
at invoker (vue.js:2029)
at HTMLParagraphElement.fn._withTask.fn._withTas
The problem is that you create a child component inside of your parent Vue that contains the template with the binding to the tes function. That means that the child will look in its own methods for tes, however it is a property of your parent, not of the child itself so it will never be able to find it in its own scope. You have to add the function to the child component instead:
const Hello = {
props: ['text'],
template: '<button v-on:click="tes"> </button> ',
methods: {
tes: function(){
alert('Teste');
}
}
};
Just expanding #Philip answer
Basically you can't access parent methods in programatically created components.
You need to specify the methods inside the child components.
const Hello = {
props: ['text'],
template: '<button v-on:click="this.tes"> Vue Generated</button> ',
methods: {
tes: function(){
alert('Teste');
}}
};
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
},
mounted(){
this.alertar()
},
methods:{
alertar: function(event){
const HelloCtor = Vue.extend(Hello);
var instance = new HelloCtor({
propsData: {
text: 'HI :)'
}
})
instance.$mount() // pass nothing
this.$refs.container.appendChild(instance.$el)
},
tes: function(){
alert('Teste');
}
}
})
Check this fiddle here
https://jsfiddle.net/50wL7mdz/370645/
However in some cases you may be able to access the parent components methods using
$parent directive which I believe will not work when components is created programatically.

Vue.js global event not working

I've got
<component-one></component-one>
<component-two></component-two>
<component-three></component-three>
Component two contains component three.
Currently I emit an event in <component-one> that has to be caught in <component-three>.
In <component-one> I fire the event like this:
this.$bus.$emit('setSecondBanner', finalBanner);
Then in <component-three> I catch it like this:
mounted() {
this.$bus.$on('setSecondBanner', (banner) => {
alert('Caught');
this.banner = banner;
});
},
But the event is never caught!
I define the bus like this (in my core.js):
let eventBus = new Vue();
Object.defineProperties(Vue.prototype, {
$bus: {
get: () => { return eventBus; }
}
});
What could be wrong here? When I check vue-dev-tools I can see that the event has fired!
This is the working example for vue1.
Object.defineProperty(Vue.prototype, '$bus', {
get() {
return this.$root.bus;
}
});
Vue.component('third', {
template: `<div> Third : {{ data }} </div>`,
props:['data']
});
Vue.component('second', {
template: `<div>Second component <third :data="data"></third></div>`,
ready() {
this.$bus.$on('setSecondBanner', (event) => {
this.data = event.data;
});
},
data() {
return {
data: 'Defautl value in second'
}
}
});
Vue.component('first', {
template: `<div>{{ data }}</div>`,
ready() {
setInterval(() => {
this.$bus.$emit('setSecondBanner', {
data: 'Bus sending some data : '+new Date(),
});
}, 1000);
},
data() {
return {
data: 'Defautl value in first'
}
}
});
var bus = new Vue({});
new Vue({
el: '#app',
data: {
bus: bus
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.28/vue.js"></script>
<div id="app">
<second></second>
<first></first>
</div>
Have you tried registering the listener in created instead of mounted?
Also, why define the bus with defineProperties and not simply:
Vue.prototype.$bus = new Vue();

vuejs listening for event on component

I am unable to catch the flash event on my custom component that i am emitting from the console . Here is my component:
/* file flash.vue */
<template>
<span :class="type" class="alert flash-alert" v-show="show">
{{ body }}
</span>
</template>
<script>
export default {
props: ['type','message'],
data() {
return {
body: this.message,
show: false,
};
},
created() {
if(this.body)
{
this.showComp();
this.hide();
}
},
methods: {
showComp: function(){
this.show = true;
},
hide: function()
{
var vm = this;
setTimeout(function() {
vm.show = false;
},2000);
},
},
events: {
flash: function(newMessage) {
this.body = newMessage;
this.showComp();
},
}
}
on the chrome console i am firing the event with:
/* from console */
window.vueEventBus = new Vue();
window.vueEventBus.$emit('flash','my message');
the event is firing as i can see that on the vue devtools tab. But upon catching the event the component should become visible, which is not.
However, if i piggyback the listener on the global event bus inside the components created() method, it works.
created() {
window.vueMessageBus.$on('flash',newMessage => {
this.body = newMessage;
this.showComp();
});
}
What should i do if i want the event listener to be registered inside the events property of the component itself?
-Thanks, Yeasir
look this example
eventbus created in index.js
Vue.prototype.$vueEventBus = new Vue()
listener on (see components/eventBus/eventBus.vue)
created() {
this.$vueEventBus.$on('message-in', this.newMessage)
},
beforeDestroy() {
this.$vueEventBus.$off('message-in')
}
emit event (see components/eventBus/childEventBus.vue)
methods: {
newMessage(something) {
this.$vueEventBus.$emit('message-in', something)
}
}
app page https://3xyx386q65.codesandbox.io/eventBus

Cannot add/remove/toggle element class inside Vue directive?

This works: https://jsfiddle.net/hxyv40ra
However, when I place this code inside of a vue directive the button event is triggered and the console shows the class is removed but nothing visually changes?
Here's an example: https://jsfiddle.net/hLga2jxq
Directive code is also below (to appease stackoverflow's rules).
styles
.hide {
display: none;
}
html
<div id="app">
<button v-hide-for="'uniqueID'">toggle to show?</button>
<div class="hide" hide-name="uniqueID">
Hello! :D
</div>
</div>
js
Vue.directive('hide-for', {
bind(button, b, vnode, oldVnode) {
console.log(b);
var elsToToggle = document.querySelectorAll(`[hide-name="${b.value}"]`);
console.log(button, b.value, `[hide-name="${b.value}"]`, elsToToggle);
button.addEventListener('click', (b) => {
console.log(button, " clicked");
elsToToggle.forEach((el) => {
console.log(el);
el.classList.toggle('hide');
})
}, false)
}
});
var app = new Vue({
name: "test",
el: '#app',
data: {}
})
So I tried this from another angle and also made the 'hide-name' attribute a directive as well, then on click I emitted the 'uniqueID' which 'hide-name' directive picked up.
I'm still not sure why Vue is not visually updating the browser but I'm guessing it must have something to do with the 'virtual-dom'.
demo: https://jsfiddle.net/hLga2jxq/3/
Vue.directive('hide-for', {
bind(el, b, vnode) {
el.addEventListener('click', (event) => vnode.context.$emit(b.value, event) );
}
});
Vue.directive('hide-name', {
bind(el, b, vnode, oldVnode) {
vnode.context.$on(b.value, function(){
let hasHideClassAttr = el.getAttribute('hide-class');
if(hasHideClassAttr) hasHideClassAttr.split(' ').forEach((c) => el.classList.toggle(c) );
else el.classList.toggle('hide');
});
}
});

Vue 2 - different behavior on $emit when fired from click or input event

In the below (fiddle here) the $emit fired by the click event below works as expected. The $emit fired by the input event is ( / seems to be) wired up the same way, but the parent doesn't receive the $emit.
<div id="app">
{{ message }}
<child-comp
:prop="property"
#emitted="receiveEmit"
#emittedFromInput="receiveEmitFromInput"
></child-comp>
{{ otherMessage }}
</div>
Vue.component('child-comp', {
template: '<div><button #click="sendEmit">emit</button><input type="text" #input="onInput"><p v-if="inputEventFired">The input event fired</p></div>',
data: function() {
return {
inputEventFired: false
};
},
methods: {
onInput: function(e) {
this.inputEventFired = true;
this.$emit('emittedFromInput', 'never see this');
},
sendEmit: function() {
this.$emit('emitted', 'can change from click event that emits');
}
}
});
new Vue({
el: '#app',
data: function() {
return {
message: 'allo',
otherMessage: 'cannot change this from input event that emits'
};
},
methods: {
receiveEmit: function(val) {
this.message = val;
},
receiveEmitFromInput: function(val) {
alert('i do not happen')
this.message = val;
}
}
});
Just to make the above more readable, the template for the child component is
<div>
<button #click="sendEmit">emit</button>
<input type="text" #input="onInput">
<p v-if="inputEventFired">The input event fired</p>
</div>
Someone outside of SO helped me on this, and I'll post their info here. The issue here is neither the events nor the emitter, but rather (my ignorance of) the case-insensitivity of html. It seems that #someCamelCasedEvent is actually passed along to the javascript as #somecamelcasedevent. working fiddle
this.$emit('emitted-from-input', 'never see this');
<child-comp
#emitted="receiveEmit"
#emitted-from-input="receiveEmitFromInput">
</child-comp>

Categories

Resources