Your code seemed to be working fine for me, but it was only handling keyboard presses and didn't work when you pasted.
A better way would be to validate the input and fix it when the input value changes with @input
since you were already binding the results to this.firstname
.
<template>
<div id="app">
Only letter
<input type="text" v-model="firstName" @input="prueba" />
<br />
</div>
</template>
<script>
export default {
name: "App",
data: () => ({
firstName: "",
}),
methods: {
prueba() {
//this will replace every non letter input with an empty string leading to removing it
this.firstName = this.firstName.replaceAll(/[^a-zA-Z]/g, "");
//you can also do your uppercase change here, no need to do it in update
this.firstName = this.firstName.toUpperCase()
},
},
};
</script>
note that you can also combine both lines in one such as
this.firstName = this.firstName.replaceAll(/[^a-zA-Z]+/g, "").toUpperCase();