I'm using Element UI radio button group and I want to use preventDefault() when trigering change event:
<el-radio-group #change="changeHandler" v-model="radio1">
<el-radio-button label="New York"></el-radio-button>
<el-radio-button label="Washington"></el-radio-button>
<el-radio-button label="Los Angeles"></el-radio-button>
<el-radio-button label="Chicago"></el-radio-button>
</el-radio-group>
Script:
methods: {
changeHandler(value, e) {
// e is undefined
// here should be preventDefault
}
}
I tried set second parameter to change function, but it is undefined.
Element UI works a bit differently. #change just returns the value chosen, nothing else. We need to use native to access the Event object. But #change.native won't work, as the change has already happened (if you want to actually prevent the change). As a side note, I would use disabled attribute instead (like presented in documentation). In my opinion for the UX it's weird that a user cannot choose a radio button, but putting that aside.... Use #click.native instead if you want to prevent a choice:
#click.native="changeHandler"
Then you have access to the Event object:
changeHandler(e) {
console.log(e.target.value)
if(e.target.value == "Washington") {
e.preventDefault();
}
}
CODEPEN
You're having the parameters in the method wrong. The are 2 ways this can go using the #change event handler:
A
defining a change handler without arguments, like
<el-radio-group #change="changeHandler" v-model="radio1" ></el-radio-group>
changeHandler(e) {
// e will be the Event object
console.log(e)
}
B
defining a change handler with arguments, put in $event to still include the event object
<el-radio-group #change="changeHandler($event, 'string')" v-model="radio1" ></el-radio-group>
changeHandler(e, value) {
// e will be the Event object
console.log(e);
// value will be 'string'
console.log(value);
}
Related
How to get Vuetify checkbox event.target.checked and event.target.value?
I'm using Vuetify's checkbox in my app. When the user checks/unchecks it, I want to get the checked and the value properties.
I realize that it is not a native checkbox element, so I don't have access to event.target.checked or event.target.value, but surely there must be a way to do that. I tried the following:
<p v-for="(linkType, index) in linkTypes" v-if='linksLoaded'>
<v-checkbox
color="info"
:label="linkType"
:value="linkType"
v-model="checkedLinks"
#click="onCheckboxClicked($event)"></v-checkbox>
</p>
...
onCheckboxClicked: function(e) {
console.log(e);
},
For some reason it printed a mouse event TWICE and the checkbox itself didn't change (the check mark wasn't unchecked).
#click.native printed the same mouse event, but once.
I tried #change="onCheckboxClicked" - that printed the v-model.
So is there a way to do that?
I see that you are looping without binding a key, and inside you have v-model which is hooked to a single variable. So, whenever some checkbox is changed all others will update simultaneously. So you need new v-model for each checkbox. Why not add another property in linkTypes so you can v-model="linkType.checked".
change is the name of the event which gets triggered whenever checkbox value is changed and value is passed as a parameter.
<v-checkbox
#change="checkboxUpdated"
color="info"
:label="linkType"
:value="linkType"
v-model="checkedLinks"
#click="onCheckboxClicked($event)"></v-checkbox>
and in methods you need
checkboxUpdated(newValue){
console.log(newValue)
}
The easy way to access whether the checkbox is checked or not after clicking is to check it value. If it is null or empty array, then you can assume that its not checked. It depends on how you initialised the variable. This is the safest way to get what you want.
<v-checkbox
v-for="(el, index) in checkboxes"
:key="index"
v-model="checkbox[index]"
:value="el"
:label="`Checkbox ${index}`"
#change="valueChanged($event, index)"
></v-checkbox>
new Vue({
el: '#app',
data () {
return {
checkboxes: ['Opt 1', 'Opt 2', 'Opt 3', 'Opt 4'],
checkbox: [],
}
},
methods: {
onChange(val, i) {
console.log(val, i, this.checkbox)
if (val === null || val.length === 0) { // Custom checks in this
console.log('Unchecked')
} else {
console.log('Checked')
}
}
}
})
If you really need access to the element, then you can use ref to get the component. Then try to find the input element inside the component. And then find the checked value of that input element. But depending on how the library is implemented, you might not get the right value for $refs.checkbox.$el.target.checked.
You are lookin for a event. If you want to know if your checkbox is checked or not, you should use this:
onCheckboxClicked: function(e) {
console.log(e.target.checked)
},
As you've already noticed, Vuetify checkbox is not a native checkbox element. Therefore, event.target.checked and event.target.value do not exist. To fix this, one needs to do 2 things:
Disable the ripple effect of the v-checkbox. Otherwise, there will be a div on top of the input checkbox tag. Then, event.target is the one we expected.
However, when the user clicks on the label, it also affects the checkbox. In this case, we need to access the checkbox via event.target.control.
The checkbox in your question should like this:
<v-checkbox
color="info"
:ripple="false"
:label="linkType"
:value="linkType"
v-model="checkedLinks"
#click.native="onCheckboxClicked"
/>
Then, in the onCheckboxClicked method:
const onCheckboxClicked = (event) => {
const target = event.target.control ?? event.target;
const isChecked = target.checked;
const value = target.value;
// Do something here...
};
Notice that we use .native modifier for the click event. Otherwise, the event.target.control.checked will give opposite values (false when the checkbox is checked and vice versa).
And small note: you should always bind the key value when using v-for.
My application dialog should respond to "Ctrl+S" for a save function and cancel the default browser Save event.
<div
#keyup.83="doSave"
#keyup.ctrl.83="doSave"
#keyup.alt.83="doSave"
tabindex="0">
The doSave event is fired on pressing 's' (and alt+s) but not on ctrl+s.
Why is ctrl+s not fired?
BONUS QUESTION: Is there a way to preventDefault without coding it? Somehow it should be possible adding .once but the docs are vague.
https://codepen.io/cawoodm/pen/qvgxPL
There are several questions packed into your question, so I am going to answer them one by one:
Why is ctrl+s not fired?
Several reasons. You are using the keyup event, while the browser starts to save the page on a keydown event. Your keyup event is thus never fired.
For any event to be registered on your div, your div must have focus, because otherwise the event will not originate from that element, but instead from (presumably) the body.
Is there a way to preventDefault without coding it?
Yes. Add the prevent modifier to your event. In the same way, you can use once to unregister an event after it has triggered once, stop to use stopPropagation() and passive to explicitly not stop propagation. You would use something like: #keydown.ctrl.83.prevent="saveMe".
How do I get this working?
If you are fine with the user having to focus the element before being able to save, and otherwise getting the default behaviour, use the following:
<div
id="app"
tabindex="0"
#keydown.ctrl.83.prevent.stop="saveInsideComponent"
>
<!-- -->
</div>
Otherwise, this is one of the few moments where registering your own event listener is useful. Just make sure to remove it before your component is destroyed, or you will have a rogue event listener throwing errors on other components + a memory leak to deal with.
mounted() {
document.addEventListener("keydown", this.doSave);
},
beforeDestroy() {
document.removeEventListener("keydown", this.doSave);
},
methods: {
doSave(e) {
if (!(e.keyCode === 83 && e.ctrlKey)) {
return;
}
e.preventDefault();
console.log("saving from DOM!");
},
}
#Sumurai8's answer is great but I would add support for apple devices by checking if e.metaKey is == true. The code then looks like this:
mounted() {
document.addEventListener("keydown", this.doSave);
},
beforeDestroy() {
document.removeEventListener("keydown", this.doSave);
},
methods: {
doSave(e) {
if (!(e.keyCode === 83 && (e.ctrlKey || e.metaKey))) {
return;
}
e.preventDefault();
console.log("saving from DOM!");
},
}
Final Edit/Solution: https://jsfiddle.net/up9xkhsm/1/
Is there such an event for v-select that I can key on when it is closed? Or some sort of a 'timer' I can set to remove validation errors after they occur?
This is the v-select I am using:
https://vuetifyjs.com/en/components/selects
Edit: this outlines the issue:
https://jsfiddle.net/96vnLm7g/
I want to know when a user clicked on the v-select but did not select anything. It has to be possible, obviously, since the validation can pick up on this..
Use an onChange prop to add your callback function, so you can check for the v-model assigned to v-select if has changed, to clear the validation errors. Or watch the v-model assigned to v-select for changes.
Using onChange:
<v-select :options="options" :on-change="cleanUpValidation" v-model="selectModel" name="some-select"></v-select>
And in VueJS
methods: {
cleanUpValidation(){
//do the cleanup
}
}
By default, onChange emits input event with value of selected option:
default: function (val) {
this.$emit('input', val)
}
So you can use it also to catch the input event:
<v-select :options="options" #input="cleanUpValidation" v-model="selectModel" name="some-select"></v-select>
In VueJS
methods: {
cleanUpValidation(val){
//do something with selected option value or cleanup error
}
}
Or you can watch the model assigned to v-select:
watch: {
'selectModel' : function(){
//do the cleanup or something with this.selectModel
}
}
For onChange and other props see:
https://sagalbot.github.io/vue-select/docs/Api/Props.html
Same thing would apply for VuetifyJS's v-select.
Edit:
Main goal was to clear validation errors when v-select is actually clicked.
v-select uses focus event within its onClick() method, to tell the VueJS that component is clicked, so that can be used to catch the click event:
<v-select
#input="inputChanged"
v-on:change="changeChanged"
label="Select Item"
:items="myItems"
required
:rules="rules.requiredField"
#focus="focusChanged"
>
</v-select>
And in js:
methods:{
focusChanged(){
console.log('focusChanged ');
},
}
For last example: https://jsfiddle.net/c5moqweu/
And see https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/components/VSelect/VSelect.js
onClick
Method
I'm trying to focus a text field on ALT + C shortcut (in my Electron-Vue app):
Codepen: https://codepen.io/anon/pen/aqrBzq?editors=1010
The codepen uses this v-text-field custom component and as this comment on Github says, the method should be wrapped with $nextTick in order to focus this component
Need this, but doesn't work
<v-text-field
ref="filter"
#keyup.alt.67="focusFilter"
label="type here" >
</v-text-field>
...
methods: {
focusFilter(event){
this.$nextTick(event.target.focus)
}
}
This works, but is not what I need:
I also tried this code below, trying to focus it with a button, and it works, but I want to make it work with a key shortcut (for example ALT + C but more preferably CTRL + F, but for the sake of example I don't use reserved shortcut)
<v-btn #click="focusFilter()">focus</v-btn>
...
methods: {
focusFilter(event){
this.$nextTick(this.$refs.filter.focus)
}
}
When you're listening for a native event on a component you need to use the .native modifier.
So use this instead:
#keyup.native.alt.67="focusFilter"
Details here: https://v2.vuejs.org/v2/guide/components.html#Binding-Native-Events-to-Components
If you want to focus this input when alt+c is pressed and anything is focused you'd need to add an event handler to window for keyup.
I'd created a method to focus it under the right circumstances like:
methods: {
listenForFocusFilterShortcut (event) {
if (event.keyCode === 67 && event.altKey) {
this.$refs.filter.focus()
}
},
}
Then, add a created hook and attach this callback to the window when there's a keyup event.
created () {
window.addEventListener(
'keyup',
this.listenForFocusFilterShortcut
)
},
Then, remove the event listener when the component is removed:
destroyed () {
window.removeEventListener(
'keyup',
this.listenForFocusFilterShortcut
)
}
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>