better way to handle multiple emits in vue - vue.js

I have emits to do if I enter a page to do some magic with a menu, I have a solution but its seems very static and too much code for such fancy modern things like vue or quasar.
On every component I need to emit a event I use this for example:
this.$root.$emit('category-one--name')
And to receive the emit event and handle stuff I use this:
this.$root.$on('category-one--name', this.setSelectBox1)
this.$root.$on('category-otherone--name', this.setSelect2)
this.$root.$on('category-more--name', this.setSelectBox3)
this.$root.$on('category-somemore--name', this.setSelect4)
this.$root.$on('category-ansoson--name', this.setSelectBox5)
then I handle stuff with the following:
setSelectBox1() {
this.model = this.categories[1]
},
setSelectBox2() {
this.model = this.categories[2]
},
Is there a better way, for example give the emitted event an Id or something and then to iterate over all in one method and not just to repeat the code?
thanks

Emit function accept a value as second param so try this:
this.$root.$emit('category-change', this.name);
Then:
this.$root.$on('category-change', this.setSelectBox);
setSelectBox(category) {
// set model here
},

Related

access methods from the vue current component

Can any one guide or suggest how to resolve this below issue.
Use Case: Trying to implement notification component
Scenario: I am trying to call a method or change the state of the data on triggering of event in Vue.
I have defined the event listener on mounted function and trying to access one of the method.
Basically, the alert within event function is getting triggered, where as alert inside method is not getting triggered, and even any data manipulation is not executing even with in event function.
Where am i missing? is it incorrect to alter state within Event listener?
Basically i am trying to implement notification feature which automatically disappear after few seconds
Any help is appreciated.
Thanks,
Girish
There is another reason,this inside callback function is not Vue component. You can assign var self = this and use inside the callback, or use arrow function.
mounted: function () {
var self = this
EventBus.$on('show', function () {
self.test()
self.show = true
})
},
methods: {
test () {
console.log('Inside methods')
}
}
I believe your problem is the spelling error instead of
method: {}
, use methods: {}
Example:
Error.
method: {
test: function () {
alert('Inside Method');
}
correct.
methods: {
test: function () {
alert('inside method);
}
}
I know it does not have much to do with the question, but be careful when using the event bus, it would be as if you had a speaker, and shouted in the middle of a crowd the name of a person.
Example:
eventbus says Hamilton in the midst of a crowd of 10,000 people.
How many Hamiltons can you have in the middle of this crowd? Use something more specific, such as parent-child communication, avoid using the event bus.

Prevent $emit from emitting more than once in Vue

I have an emit call within my Vue project to update search results, but the emit is being called at least 4 times, because the emit call is defined at various spots, so the data is sometimtes emitted with it and at other spots it is not.
I am using a global bus to perform an emit.
this.$root.bus.$emit('resetValuesInInfiniteLoader', filteredEntities);
this.$root.bus.$on('resetValuesInInfiniteLoader', function (filteredEntities) {});
I tried to name the emits calls differently, and also tried to use a different global vue bus but both options did not really work well.
Any idea how I can do this in an efficient manner so that the $emit is always only called once? How do I need to set it up to ensure that the emit is always only called once? I also tried using $once, which did not work, or tried to destroy the $emit. Can someone give me a small fiddle example or so maybe so I understand how to do this in the right way?
I have found this to be the case also and feel that there are some problems with using it in multiple locations. My understanding is that global event busses are not recommended in most applications as they can lead to a confusing tangle of events. The recommendation is that you use a state management solution like vuex.
But anyway, just a couple of points with your code above. I don't know how you created your bus but I have known to create it as such:
//main.js
const EventBus = new Vue()
Object.defineProperties(Vue.prototype, {
$bus: {
get: function () {
return EventBus
}
}
})
This creates it and makes it global. It can then be triggered in a component or components with:
<button #click="$bus.$emit('my-event')">click</button>
or
methods: {
triggerMyEvent () {
this.$bus.$emit('my-event', { ... pass some event data ... })
}
}
and listened to:
created () {
this.$bus.$on('my-event', ($event) => {
console.log('My event has been triggered', $event)
this.eventItem = 'Event has now been triggered'
//this.$bus.$off('my-event')
})
},
I have found that it works sometimes. I don't know why but it will work then it will trigger several events and I think it is because it isn't finalised or something. You may note I have commented out this.$bus.off which certainly stops it but it then doesn't work again. So I don't know what that's all about.
So there you go, a total non-answer, as in, Yes I've had that too, No I cant fix it.
I went with using vuex store, it seems a lot easier to communicate with any component within the application, has the advantage of global communication, yet does not have the caveat of sending multiple actions such as emit events

Component to emit event on mapGetters

So I load my component, I then call the do something like the following:
created() {
this.$store.dispatch('messages/connect');
this.$store.dispatch('messages/fetchAllMessages');
// this.$emit('set-recipient', this.chats[0]);
},
computed: mapGetters('messages', {
chats: 'getMessages'
}),
The commented section within created is the snippet that I would like to run but only on the creation of this.chats and not on any update there after.
If I try to emit the event where it currently is I get an error: Cannot read property '0' of null.
Hopefully you understand what I mean.
Any ideas?
fetchallMessages calls your server to get the messages, right? that asynchonous process won'T be finshed when the meit is run like that.
If you make sure to return a Promise from that action which resolves after you have added chats, you can do this:
this.$store.dispatch('messages/fetchAllMessages')
.then(() => {
this.$emit('set-recipient', this.chats[0]);
})
If you have trouble returning a Promise from that action, share its implementation and we'll fix it.
If i understand correctly, you want to execute this.$emit('set-recipient', this.chats[0]); only after chats was initialized.
You have 2 options:
don't use mapGetters for the chats getter, just define the computed yourself:
computed: {
...mapGetters('messages')
chats(){
const messages = this.$store.getters.getMessages;
if (messages.length){
this.$emit('set-recipient', this.chats[0]);
}
return messages;
}
}
Instead of doing it in the component, you can move the logic to the store and emit the event from there when you modify chats

Event handlers and components in vue.js

How would I specify a kind of change/input handler to be used by a component in vue.js, when I already have one in place in the component? I have a text input which in very simplified form is akin to this:
Vue.component('text-input', {
template: '\
<span>\
$\
<input\
ref="input"\
v-bind:value="value"\
v-on:input="updateValue($event.target.value)"\
>\
</span>\
' ,
props: ['value'],
methods: {
updateValue: function (value) {
this.$emit('input', value);
},
uppercase:function(value){
return value.toUpperCase();
}
}
});
I would use it like this:
<text-input v-model="name"></text-input>
It works fine, and updates correctly using the events system (with $emit). But now I want to convert the entered value to uppercase, so would presumably want to pass in an input handler which is the name of the method, such as 'uppercase', in my 'text-input' component. So I would have this:
<text-input v-model="name" #input="uppercase"></text-input>
But within my component, #input is already used by updateValue. How can I combine the two? (Or, perhaps, is there a different and better way of thinking this whole problem?)
I'm having a bit of a hard time following, but if you wanted to run two functions on the same event you can just do two things in one function.
#input="update"
Then something like:
update: function (event) {
this.updateValue(event.target.value)
this.somethingElse()
}
Though, if the code you have is really what you're trying to do you can do:
this.$emit('input', this.uppercase(value))
After discussing a bit more in the comments. You can pass a custom callback as a property if you'd like and call that instead of whatever the default is. Here's a quick fiddle of one way to approach that: https://jsfiddle.net/crswll/3xwmgpom/

Jquery .off vs dojo?

What is equivalent of jquery $.off(event) to remove event on element by passing event name in Dojo?
I tried :
dojo.disconnect(handle) // but I dont have an handle to event
How to get the handle or is there any better way to to it?
There is no out of the box solution as far as I know of, so you would have to implement one by yourself. However, this could be a dangerous feature, if you suddenly disconnect all event handlers of a specific type.
However, you could use the dojo/aspect module to intercept calls to the dojo/on module, for example:
aspect.around(arguments, 0, function(original) {
on.signals = [ ];
return function(dom, name, handler) {
console.log(arguments);
on.signals.push({
signal: original.apply(this, arguments),
name: name
});
};
}, true);
I didn't find a proper way to put an aspect around a function itself, rather than a function wrapped inside an object. So I used a dirty trick and used the arguments array and because the on module is my first argument, this will put an aspect around the dojo/on reference.
What happens is that when you bind an event handler using dojo/on, it will save it inside an array. Now you could write your own dojo/on::off() function, for example:
on.off = function(eventName) {
arrayUtils.forEach(on.signals, function(signal) {
if (signal.name === eventName) {
signal.signal.remove();
}
});
};
Now you can use:
on.off("click");
To disconnect all click event handlers.
A full example can be found on JSFiddle: http://jsfiddle.net/Lj5yG/ but this could probably be improved.