Vue- variables inside event bus are not reactive - javascript

I created global event bus
Vue.prototype.$eventHub = new Vue()
In 'component 2' clicking on the button, emitted the event then navigating to 'component 1'
this.$eventHub.$emit('deleted')
In another 'component 1' i am trying to use it
data: function () {
return {
testVariable : true
}
},
beforeMount () {
var self = this
this.$eventHub.$on('deleted', function () {
console.log("executed")
self.testVariable = false
})
}
In the template section i used {{testVariable}}, it's always displaying true. Why it is not reactive? what did i miss?

Related

Vue 3 Passing Additional Data as a Parameter with Object Syntax Dynamic Events

Is there a way to pass additional data to event handlers when using the object structure syntax?
Example:
MySFC.vue:
<template>
<template v-for="action in actions" : key="action.id">
<button v-on="action.eventHandlers"> <!--Looking for some way to pass both event and additional data here-->
{{ action.name }}
</button>
</template>
</template>
<script>
import { actions } from '#/lib/my-actions.js';
export default {
setup(props, ctx){
return{
actions,
}
},
}
</script>
my-actions.js:
export const actions = {
name: 'my action',
eventHandlers: {
click: (event, myData) => {
console.log(myData);
},
mousedown: (event, myData) => {
console.log(myData);
}
}
}
Normally if you have a specific event you are trying to handle you can just do something like:
<button #click="myClickHandler($event, myData)"> </button>
But in this case I am trying to build a wrapper of sorts for plugins and do not know what events may need to be handled, so I can't pre-define with v-on which events to look for.
I saw some options for attaching dynamic events, but I only see examples where the event itself is passed, not with additional parameters.
You could use Function.prototype.bind() to bind the function's initial arguments. This can only be done with regular functions (not arrow functions).
// #/lib/my-actions.js
const myClickData = {
id: 'my click data',
}
const myMouseDownData = {
id: 'my mousedown data',
}
export const actions = [
{
name: 'my action',
eventHandlers: {
click: function (myData, event) {
console.log('click', { event, myData })
}.bind(undefined, myClickData),
mousedown: function (myData, event) {
console.log('mousedown', { event, myData })
}.bind(undefined, myMouseDownData),
},
},
]
demo

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.

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

vuejs - remount after update background url

I have a vue component with the binding style:
<div id="profile-icon" :style="{'background': 'url('+profile.icon+')'}"></div>
<input name="name" v-model="profile.name" type="text" placeholder="Name">
The image will only display if I update the profile.icon under beforeMount function:
props: ['profileprops'],
data: function() {
return {
profile: {
address:'',
name:'Name',
description:'Short description',
address:'address',
user_id:'',
background:'/images/admin/bg.png',
icon:''
},
}
},
beforeMount: function() {
console.log('profileedit mounted.')
var self = this;
if(self.profileprops){
self.profile = self.profileprops;
};
if(!self.profile.icon){
self.profile.icon = '/images/admin/icon-144.png';
},
mounted: function() {
var self = this;
self.$eventHub.$on('ImageSelected', (imagelink) => {///<-- I have another vue component for selecting images to emit event and to catch it here.
self.profile.icon = imagelink;
$('#profilemodal').modal('hide');
});
},
watch: {
'profile': {
handler: function(newVal, oldVal) {
console.log("changed")
},
deep: true
},
}
I did a few tests:
if I select the image, I can see the console.log within the $on has given the correct data from profile.icon. However, the :style did not update the background image with the new profile.icon. And watch.profile did not react at all.
If I start to type on input with v-model bind with profile.name, everything update immediately, including the background image in :style.
I believe I must have miss something to sync the inside and outside of the $on. How can I resolve this?
I found the solution here.
So I just $forceUpdate() inside $on:
self.$eventHub.$on('ImageSelected', (imagelink) => {
self.profile.icon = imagelink;
self.$forceUpdate();//<-- this does the magic.
});
then the data will update and re-render.

Scope of 'this' on onTap and on popUp in ionic is 'undefined'

I want to show a popUp in ionic, which does not allow the user to exit when he hasn't entered some input. Right now I'm using this here:
public showOwnIdentifierPrompt() {
// Prompt popup code
var promptPopup = this.$ionicPopup.prompt({
title: this.floor_name,
template: `<input ng-model="$ctrl.customFloorName"></input>`,
scope: this.$scope,
buttons: [
{
text: this.cancel,
type: 'button-clear button-balanced',
onTap: function(e) {
// Cancel creation
return false;
}
},
{
text: this.save,
type: 'button-clear button-balanced',
onTap: () => {
// Create new floor
return true;
}
}
]
});
promptPopup.then((res) => {
if (res) {
this.addNewFloor(this.customFloorName);
}
})
}
In the save onTap() event handler, I would like to access this.customFloorName from my class, to decide whether the user entered input. But it is always undefined. What can I do?
You can get value on Save with below code :
var value = this.scope.$ctrl.customFloorName;

Categories

Resources