Why is "event" accessible in Vue v-on methods even without the argument? - vue.js

According to the page on event handling in the docs for Vue, when you use v-on like v-on:click="handler" the handler function will automatically get the original DOM event as the first argument. This code snippet is directly adapted from those docs.
new Vue({
// Vue config shortened for brevity
methods: {
handler(event) {
// `this` inside methods points to the Vue instance
alert('Hello ' + this.name + '!')
// `event` is the native DOM event
if (event) {
alert(event.target.tagName)
}
}
}
})
Why the heck can I still access event even if I omit it from the functions parameter list like this:
handler() {
console.log(event); // Still returns the native DOM object even though
// I don't explicitly define `event` anywhere
}
Shouldn't event be undefined if I don't add it as an argument to the function?

I believe that'll be the global window.event:
https://developer.mozilla.org/en-US/docs/Web/API/Window/event
Nothing to do with Vue, it's just an unfortunate coincidence that you happened to call it event.

Maybe the docs explains the reason to use event in the handler function as first argument:
You should avoid using this property in new code, and should instead use the Event passed into the event handler function.
https://developer.mozilla.org/en-US/docs/Web/API/Window/event

Related

How can I implement arrow function in VueJS?

My method in vue looks like this :
methods: {
setDate: async function () {
console.log(this.modal)
}
}
I want to change it to an arrow function. I tried like this :
methods: {
setDate: async () => {
console.log(this.modal)
}
}
There exist error like this :
Cannot read property 'modal' of undefined
How can I solve this problem?
use function directly like
methods: {
async setDate() {
console.log(this.modal)
}
}
You are facing this error because an arrow function wouldn't bind this to the vue instance for which you are defining the method. The same would happen if you were to define computed properties using an arrow function.
Don’t use arrow functions on an instance property or callback e.g.
vm.$watch('a', newVal => this.myMethod())
As arrow functions are bound to the parent context, this will not be the Vue instance as you’d expect and this.myMethod will be undefined.
You can read about it here.
This link https://michaelnthiessen.com/this-is-undefined/ says the following:
"An arrow function uses what is called lexical scoping. We'll get into this more in a bit, but it basically means that the arrow function takes this from it's context.
If you try to access this from inside of an arrow function that's on a Vue component, you'll get an error because this doesn't exist!
So in short, try to avoid using arrow functions on Vue components. It will save you a lot of headaches and confusion."

How could the Vue app pass the additional parameters to the event listener when using vm.$once()?

In the Vue app , it could add the event listener that is called one time by using vm.$once.
For example, in the child component of Vue App, it could emit the event using vm.$emit('myEvent', data). emit event
And then it could be processed by adding the event listener to this event using vm.$once in the Vue app.
At this time, I want to pass the additional data to event handler.
I've tried as following. But it throws an error.
Uncaught ReferenceError: $event is not defined
//In the Vueapp
this.$refs.child.$once(
'myEvent', this.eventHandler($event, additional_data)
);
But I think that it could pass the additional data when using v-on.
<child-component ref="child" v-on:myEvent="eventHandler($event, additional_data)"></child-component>
//$event is the data emitted from the event , "myEvent".
When using vm.$once or vm.$on, couldn't it pass the additional parameter?
Note: the reason that I use vm.$once is that I want to execute the eventHandler only one time when emitting the custom event, myEvent and add it dynamically .
You need to capture the arguments passed into the event handler. You can do this by using an anonymous function for the handler that calls your method. For example
this.$refs.child.$once('myEvent', $event => {
this.eventHandler($event, additional_data)
})
You could also get fancy with Function.prototype.bind() however you would have to reverse the arguments list as bind prepends arguments.
For example
methods: {
eventHandler(data, $event) { // reversed argument list
// etc
}
}
and
this.$refs.child.$once('myEvent', this.eventHandler.bind(this, additional_data))

Passing a function reference through data attribute in Vue

I am trying to pass a function into recaptcha to be used as a callback. I need to write:
data-callback="function"
In Vue how do I add the function reference?
I've tried:
data-callback="{{ this.submitFocus }}"
data-callback="this.submitFocus"
I'm using Vue 2
Recaptcha2 uses the data-callback string to call a globally available function.
From what I can see in the documentation, it doesn't look like there's a programmatic way to set this so you might have to use something like this
beforeMount () {
window.submitFocus = () => { // using arrow function to preserve "this"
this.submitFocus()
}
},
beforeDestroy () {
delete window.submitFocus
}
with
data-callback="submitFocus"
in your template. The attribute value just needs to match the function added to window.
data-callback is an html attribute of a DOM element, it's just a string. It does not know about the context of your object instance, ie. this.
So, you can't use this when setting the attribute for your ReCaptcha, it will only understand functions that can be called without this.
If you had a function defined as
function submitFocus(){ ... }
globally, you could get ReCaptcha to call it by setting data-callback to submitFocus without the reference to this.
data-callback="submitFocus"

How can I capture click event on custom directive on Vue.js?

I am trying to learn Vue.js and came to an practice example where I need to implement a custom directive whice works lice 'v-on'.
This means that i need to capture the click event on my custom directive and call a method.
The template i was thinking of.
<template>
<h1 v-my-on:click="alertMe">Click</h1>
</template>
The problem is i don't know how to capture the click event in the custom directive. Excuse the clumsy code below.
<script>
export default {
methods: {
alertMe() {
alert('The Alert!');
}
},
directives: {
'my-on': {
bind(el, binding, vnode) {
console.log('bind');
el.addEventListener('click',()=>{
console.log('bind');
vnode.context.$emit('click');
});
},
}
}
}
</script>
Can anyone help me understand how this works? I didn't manage to find any example of something similar.
After some more searching i came to this solution:
<template>
<h1 v-my-on:click="alertMe">Click me!</h1>
</template>
<script>
export default {
methods: {
alertMe() {
alert('The Alert!');
}
},
directives: {
'my-on': {
// Add Event Listener on mounted.
bind(el, binding) {
el.addEventListener(binding.arg, binding.value);
},
// Remove Event Listener on destroy.
unbind(el, binding) {
el.removeEventListener(binding.arg, binding.value);
}
}
}
}
</script>
The solution you found is, as far as I can tell, the very best solution for what you are looking for. However, for those who don't know much about Vue.JS I thought I'd give a quick explanation. I'd also suggest you check out the official Vue documentation for Custom Directives or my Medium article on the concepts.
This is the code that Vlad came to and I would support:
<template>
<h1 v-my-on:click="alertMe">Click me!</h1>
</template>
<script>
export default {
methods: {
alertMe() {
alert('The Alert!');
}
},
directives: {
'my-on': {
bind(el, binding) {
let type = binding.arg;
let myFunction = binding.value;
el.addEventListener(type, myFunction);
}
}
}
}
</script>
In short, Vue Directives are called on the lifecyle of the element they are attached to, based on the directive object definition. In the example the function defined is called "bind" so the directive will call that function when the element is bound into the DOM.
This function receives the element it's attached to "el" and the different content of the directive usage in the template "binding". In the binding usage in the template, the value after the colon ":" is the "arg" which in this example is the string literal "click". The value inside of the quotes '""' is the "value" which in this case is the object reference to the function "alertMe".
The vars that are defined by getting binding.arg and binding.value (with their respective content) can then be used to create an event listener contained inside of the element "el" that the directive is used on (el is modifiable). So, when the element is created and bound, this new event listener is created on the "click" event defined by "arg" and it will call the "alertMe" function defined by "value".
Because the modification is contained inside the element, you don't have to worry about cleaning up on unbind, because the listener will be destroyed when the element is destroyed.
And that is a basic description of what is happening in the suggested code. To see more about directives and how to use them, follow the suggested links. Hopefully that helps!
You need to register a listener for the event being emitted within your directive.
// emit a custom event
// binding.expression is alertMe
vnode.context.$emit(binding.expression);
// listen for the event
export default {
created(){
this.$on('alertMe', event => {
this.alertMe()
})
},
....
}
This is not calling the method alertMe, rather passing alertMe through to the directive as the binding expression:
<h1 v-my-on:click="alertMe">Click</h1>
#Vlad has an excellent solution!
May I also add an important point: if you wanna pass parameters to your callback, it will confuse you by the way Vue handles your expression. In short, for custom directives, whatever in between quotation marks gets evaluated and the resulted value is passed in (hence, you can get it via binding.value (duh!), while for built-in directives, at least for v-on, the contents between quotation marks get evaluated later on, when event is fired.
Maybe this is best demonstrated with a comparison between custom directive and the built-in v-on directive. suppose you have a "my-on" directive written exactly as what #Vlad does, and you use it side by side with v-on:
built-in:
<h1 v-on:click="myAlert('haha')"> Click me!</h1>
It works as expected, when button is clicked, alert window pops up.
customized:
<h1 v-my-on:click="myAlert('haha')">Click me!</h1>
As soon as button is displayed, the alert window pops up, and when you click on it, the event is fired but nothing visible happens. This is because "myAlert('haha')" is evaluated as soon as binding(?), hence the alert window, and its value gets passed to your directive(undefined or whatever), cuz its value is not a function, nothing seems to happen.
now, the workaround is to have whatever in between the quotation marks returns a function upon evaluation, eg v-my-on:click="() => {myAlert('haha')}"
Hope it helps.
References:
https://stackoverflow.com/a/61734142/1356473
https://github.com/vuejs/vue/issues/5588
As #Vlad said it worked for me:
el.addEventListener('click',()=>{
console.log('bind');
vnode.context.$emit('click');
Here's my directive:
Vue.directive('showMenu', {
bind: function (el, binding, vnode) {
el.addEventListener('click', () => {
console.log('bind')
setTimeout(() => {
this.$emit('toggleDrawer')
}, 1000)
})
}
})
Thanks dude!
Seems like addEventListener works only for native events
To catch events fired with Vue inside the directive use $on
newdirective: {
bind(el, key, vnode){
vnode.componentInstance.$on('event-fired-from-component', someFunction)
},
....
}
You can put this code either inside your component or mixin under directives section like this
directives: {...}
And then connect it to the component you want to receive this event from
<some-component
v-newdirective
></some-component>

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.