Enter with #keyup event not working in Vue - javascript

I am trying to call method on pressing enter key but it's not working. Code is as below.
<template>
<div>
<button #click="callEvent" #keyup.enter="callEvent"> Click </button>
</div>
</template>
<script>
export default{
methods:{
callEvent(){
console.log("Event called");
}
}
}
</script>

The click event already triggers with the ENTER key (it also triggers with Space in some browsers, like Chrome for desktop). So, your code only needs a #click="callEvent" and everything works well since the focus is already on the button:
var app = new Vue({
el: "#app",
methods: {
callEvent() {
console.log("Event called");
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="app">
<button #click="callEvent">Enter</button>
</div>
If you want that any ENTER triggers the button even if it isn't with focus, you should bind the event to the window object, which can be made inside the mounted handler:
var app = new Vue({
el: "#app",
methods: {
callEvent() {
console.log("Event called");
}
},
mounted() {
window.addEventListener('keyup', function(event) {
if (event.keyCode === 13) {
app.callEvent();
}
});
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="app">
<button>Enter</button>
</div>
Remember that if you're using Single File Components, the instance is exposed by the this keyword, which can be used to call component methods inside the desired handler:
export default {
methods: {
callEvent() {
console.log('Event called')
}
},
mounted() {
window.addEventListener('keyup', event => {
if (event.keyCode === 13) {
this.callEvent()
}
})
}
}

Buttons don't have keyup event on them. Even when you have focus on the button, and hit enter, it will be considered a click event, instead of keyup.enter.
Try binding the event to an input and it'd work.
Alternatively, you could use jQuery (or Plain JS) to bind for keydown event on the body element, and trigger the Vue method by calling app.callEvent().
var app = new Vue({
el: "#app",
methods: {
callEvent() {
console.log("Event called");
}
},
mounted() {
var self = this;
window.addEventListener('keyup', function(event) {
if (event.keyCode === 13) {
self.callEvent();
}
});
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="app">
<template>
<div>
<button #click="callEvent"> Click </button>
</div>
<input type="text" #keyup.enter="callEvent" />
</template>
</div>
Updated to use mounted instead of relying on jQuery - as per Erick Petrucelli's answer as it allows referring to the Vue component without the global variable.

I experienced inconsistent results when using native JS with window.addEventListener. VueJS natively supports modifying behavior for keyboard events https://v2.vuejs.org/v2/guide/events.html#Key-Modifiers
This also worked a lot better in my case due to needing separate behavior for the tab key.
Your input can look like this with custom modifiers on each key up|down
<input
type="text"
class="form-control"
placeholder="Start typing to search..."
v-model="search_text"
#focus="searchFocus"
#blur="searchFocusOut"
v-on:keyup.enter="nextItem"
v-on:keyup.arrow-down="nextItem"
v-on:keyup.arrow-up="nextItem"
v-on:keydown.tab="nextItem"
>
Then inside NextItem you can reference the event, and get each key.. or write a separate function for each key modifier.

#keyup.enter="callEvent"
change to
#keypress.enter.prevent="callEvent"
<template>
<div>
<button #click="callEvent" #keypress.enter.prevent="callEvent"> Click </button>
</div>
</template>
Ref: https://github.com/vuejs/vue/issues/5171

Related

VUE how to evoke keydown method

i would like to create method which will run when any keyboard key will down, and then i will check which key was down.
What is important, it should run even if focus will be out of any inputs and so on.
I've created evoking when focus is in 'input' using:
#keydown.native="keymonitor"
but i would like to evoke 'keymonitor' method when there is no focus on input, but coursor is anywhere on website and focus is anywhere.
How to do it? if a add
#keydown.native="keymonitor"
to general div or body, it doesn't work.
Thanks for help.
You can use the normal EventTarget#addEventListener on window as follows:
new Vue({
el:"#app",
created() {
window.addEventListener('keydown', e => {
console.log(e.keyCode);
});
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app"></div>
You can use the keydown event.
Here is an example within a Vue component:
<template>
<div>
{{ lastKeyPressed }}
</div>
</template>
<script>
export default {
data() {
return {
lastKeyPressed: null,
}
},
created() {
window.addEventListener("keydown", (e) => {
this.lastKeyPressed = e.keyCode;
console.log(e.keyCode);
});
}
};
</script>
Be sure to use an arrow function on the addEventListener callback to ensure that this keyword works correctly, see this for other examples.

Vue.js blur with enter event - triggers both of them

I am trying to make it so that when I press enter OR if I lose focus from an element it triggers a function but when I press enter it then triggers the blur event also. So the function is called twice. It should only be called once.
<input v-on:blur="saveField('name')" keyup.enter="saveField('name')">
The problem is that my saveField() function hides the element, triggering also the blur event.
I guess the other issue is how not to have to write the same function call twice. (DRY).
You can use some kind of condition to check if the value needs to be updated. It seems complicated to avoid the two events from being triggered:
<div id="app">
<input v-model="inputValue" type="text" #blur="save()" #keyup.enter="save()">
<div v-for="saving in savings">
{{ saving }}
</div>
</div>
new Vue({
el: '#app',
data: {
inputValue: '',
savedValue: '',
savings: []
},
methods: {
save () {
if (this.inputValue !== this.savedValue) {
this.savings.push('Saving value ' + this.inputValue)
this.savedValue = this.inputValue
}
}
}
})
Here is a working JsFiddle: https://jsfiddle.net/L6kfz48m/

vue.js keyup, keydown events one character behind

I'm using the keydown/keyup events which call a javascript function that prints the value of input box to the console (and also the value of the currentTarget field of the event), and I am noticing it is a character late. For example, if I type hello into the input box, I only see hell in the console, until I press another key and then I see hello, even though by this point I've typed hello1. Why is this? And is there anyway around it?
Here's the HTML:
<input type="text" class="form__field" v-model="keywords" v-on:keyup.enter="queryForKeywords" v-on:keydown="queryForKeywords">
And the JS:
queryForKeywords: function(event) {
var self = this;
if (this.keywords.length > 2) {
console.log("keywords value: " + this.keywords);
console.log("event value: " + event.currentTarget.value);
}
Because you are depending on the input's v-model to update the keywords property, the value won't update until the Vue component has re-rendered.
You can access the updated value of keywords in a callback passed to this.$nextTick like in this example:
new Vue({
el: '#app',
data() {
return { keywords: '' }
},
methods: {
queryForKeywords: function(event) {
this.$nextTick(() => {
if (this.keywords.length > 2) {
console.log("keywords value: " + this.keywords);
}
});
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>
<div id="app">
<input type="text" class="form__field" v-model="keywords" v-on:keyup.enter="queryForKeywords" v-on:keydown="queryForKeywords">
</div>
The real problem doesn't has to do with vue.js at all
The problem hides behind the keydown event!
So when the event fires, the input value is NOT updated yet. Fiddle example
MDN - keydown event
In general, keydown it is used for informing you which key is pressed. And you can access it like this:
document.addEventListener('keydown', logKey);
function logKey(e) {
console.log(e.key)
}
As solution, you can use the keyup event: Fiddle
My recommendation is to use a custom v-model using :value and the #input event.
<input type="text" :value="keywords" #input="queryForKeywords">
And the script:
data: {
keywords: ''
},
methods: {
queryForKeywords(event) {
const value = event.target.value
this.keywords = value
if (value.length > 2) {
console.log("keywords value: " + this.keywords);
}
}
}
See it in action
The currently accepted answer is for an old version of vue, in the latest versions should be used #input instead of keypress or keyup.

Check if a component has an event listener attached to it

Assuming there's some <Form> component. It can be called with a #cancel event listener attached to it and if it's the case, I want to show the cancel button that triggers this event. If there's no #cancel event, the cancel button should not be visible.
Is there a way to check if a component has event listener attached to it?
Currently I do:
<template>
<form>
<button v-if="cancelEventPassed" #click="$emit('cancel')">Cancel</button>
</form>
</template>
And call it like this:
<Form :cancelEventPassed="true" #cancel="handle_cancel" />
either
<Form/>
Is it possible to achieve this without using any additional property like cancelEventPassed?
When there are listeners attached to a component they are available in the $listeners property of the component.
You can use that property to determine if a specific listener is available. For example, here is a computed property that checks for the existence of a cancel listener.
computed:{
hasCancelListener(){
return this.$listeners && this.$listeners.cancel
}
}
And here is an example of that used in a component.
console.clear()
Vue.component("CustomForm", {
template:`
<div>
<h1>Custom Form</h1>
<button v-if="hasCancelListener" #click="$emit('cancel')">I have a listener!</button>
</div>
`,
computed:{
hasCancelListener(){
return this.$listeners && this.$listeners.cancel
}
},
})
new Vue({
el: "#app",
methods:{
onCancel(){
alert('canceled')
}
}
})
<script src="https://unpkg.com/vue#2.4.2"></script>
<div id="app">
<custom-form #cancel="onCancel"></custom-form>
<hr>
<custom-form></custom-form>
</div>
In Vue 3, the $listeners object has been removed. The listeners are now part of the $attrs object and are prefixed with on.
In order to check if a particular listener is present or not in a child component, you can do:
computed: {
hasCancelListener() : boolean {
return (this.$attrs && this.$attrs.onCancel) as boolean
}
}
The child component is called as:
<custom-form #cancel="onCancel"></custom-form>
This example for Vue 3
this.$attrs.onCancel
Explanation
Vue 3 has removed the $listeners object instead listener will be parted from the $attrs object with the on.. prefix.
Reference
https://v3-migration.vuejs.org/breaking-changes/listeners-removed.html#overview
You can check if listener exists like that:
this._events['listener-name']
If you're here looking for Vue 3 script setup or setup function solution, you can check the attrs key in getCurrentInstance function
<template>
<form>
<button #click="$emit('cancel')">Cancel</button>
</form>
</template>
<custom-form #cancel="onCancel"></custom-form>
onMounted(() => {
const instance = getCurrentInstance() // only available inside lifecycle hooks
console.log(instance?.attrs?.onCancel)
})

iron-form 2.0 in Polymer 2 element: function not defined

Trying to replicate on of the iron-form demos from https://www.webcomponents.org/element/PolymerElements/iron-form/demo/demo/index.html inside a Polymer 2 element, and just can't seem to get it working.
When clicking Submit, I get Uncaught ReferenceError: _delayedSubmit is not defined. Any suggestions?
JSBin: https://jsbin.com/pinasum/edit?html,console,output
Code:
<dom-module id="spp-login">
<template>
<iron-form id="loginForm">
<form action="/login" method="post">
<paper-input name="username" label="Username" required auto-validate></paper-input>
<paper-input name="password" label="Password" required auto-validate></paper-input>
<paper-button raised onclick="_delayedSubmit(event);" disabled id="loginFormSubmit">
<paper-spinner id="spinner" hidden></paper-spinner>
Submit
</paper-button>
<paper-button raised onclick="loginForm.reset();">Reset</paper-button>
</form>
<div class="output"></div>
</iron-form>
</template>
<script>
class SppLogin extends Polymer.Element {
static get is() {
return 'spp-login';
}
static get properties() {
return {
username: String,
password: String,
};
}
connectedCallback() {
super.connectedCallback();
const loginForm = this.$.loginForm;
const spinner = this.$.spinner;
const loginFormSubmit = this.$.loginFormSubmit;
loginForm.addEventListener('iron-form-submit', (event) => {
this.querySelector('.output').innerHTML = JSON.stringify(event.detail);
spinner.active = false;
spinner.hidden = true;
loginFormSubmit.disabled = false;
});
loginForm.addEventListener('change', (event) => {
loginFormSubmit.disabled = !loginForm.validate();
});
loginForm.addEventListener('iron-form-presubmit', (event) => {
event.preventDefault();
console.log('here');
});
}
_delayedSubmit(event) {
const loginForm = this.$.loginForm;
const spinner = this.$.spinner;
spinner.active = true;
spinner.hidden = false;
loginForm.disabled = true;
// Simulate a slow server response.
setTimeout(function() {
loginForm.submit();
}, 1000);
}
}
window.customElements.define(SppLogin.is, SppLogin);
</script>
</dom-module>
To add event listeners to DOM elements, you have to use on-event annotations in your template.
Also, whether you provide a name for the event object or not, the event object is already being passed to your callback.
Polymer doesn't support passing arguments in the event attributes.
The examples shown in the iron-form demo page is using demo-snippet, that works both for native elements as well as polymer elements.
So, you need to change your code from:
onclick="_delayedSubmit(event);"
to: on-click="_delayedSubmit".
when triggering an event with paper button you have to use on-click and you can't specify the parameters.
So the correct syntax would be on-click="_delayedSubmit"

Categories

Resources