Vue Js - focusing on element programatically - vue.js

I am brand new to using VueJs (first day!)
I want to validate an email field, and return focus to the email inut if not valid. I am using the watch property (see below), and although I can successfully watch value changes, I am not able to set focus back to the email field.
What am I doing wrong?
Code snippet
<!DOCTYPE html>
<head>
<title>V3 Example</title>
<script src="https://unpkg.com/vue#3"></script>
</head>
<style type="text/css">
form input, form button {
display: block;
margin: 3px;
}
</style>
<body>
<div id="app">
<form #submit.prevent >
<h3></h3>
<input type="username" v-model="username" />
<input ref="email" type="email" v-model="email" />
<input type="password" v-model="passwd" autocomplete="off"/>
<button #click="logIt" >Login </button>
</form>
</div>
<script>
let app = Vue.createApp({
data() {
return {
username: '',
email: '',
passwd: '',
}
},
methods: {
logIt() {
console.log('here!');
}
},
watch: {
email(val1, val2){
if (!val2.includes('#')) {
this.$refs.email.focus(); // <- this is supposed to return focus to email input
console.log('Not valid email!');
}
else {
console.log(val1);
console.log(val2);
}
}
}
})
app.mount('#app');
</script>
</body>
</html>

Try this:
this.$refs.email.$el.focus();

Here is a solution that works, I've added a method onSubmit which is called when the form submits and inside it I validate email field, and focus it if is not valid. The key here is nextTick which makes sure to wait before Vue does any DOM update it needs before focusing the element.
<!DOCTYPE html>
<head>
<title>V3 Example</title>
<script src="https://unpkg.com/vue#3"></script>
</head>
<style type="text/css">
form input, form button {
display: block;
margin: 3px;
}
</style>
<body>
<div id="app">
<form #submit.prevent="onSubmit" >
<h3></h3>
<input type="username" v-model="username" />
<input ref="email" type="email" v-model="email" />
<input type="password" v-model="passwd" autocomplete="off"/>
<button #click="logIt" >Login </button>
</form>
</div>
<script>
let app = Vue.createApp({
data() {
return {
username: '',
email: '',
passwd: '',
}
},
methods: {
logIt() {
console.log('here!');
},
onSubmit() {
if (!this.email.includes('#')) {
this.$nextTick(() => { // must wait for next tick before interacting with DOM
this.$refs.email.focus();
console.log('Not valid email!');
})
}
}
},
watch: {
email(val1, val2){
if (!val2.includes('#')) {
this.$refs.email.focus(); // <- this is supposed to return focus to email input
console.log('Not valid email!');
}
else {
console.log(val1);
console.log(val2);
}
}
}
})
app.mount('#app');
</script>
</body>

Related

Vuejs keyup/ v-on:keyup event is not working

I am trying to initiate a function from methods while an input is keyup, But it's not working. My codes from template are :
<q-input type="number" min="1" dense borderless debounce="300" class="q-ma-xs" v-
model="invoice_product.item_qty" placeholder="quantity" filled
#keyup="calculateLineTotal(invoice_product)" />
My method :
<script>
export default {
setup() {
return {
invoice_product: {
item_qty: ''
}
}
},
methods: {
calculateLineTotal(invoice_product) {
alert(invoice_product.item_qty)
}
}
}
</script>
I also tried with v-on:keyup
enter code hereYou can use watch property using your v model variable and there you can write your logic.
When your model value change it will called watch property
watch:{
“Variable” : function(val) {
//method
}
}
Try to replace setup with data:
new Vue({
el: '#q-app',
data() {
return {
invoice_product: {item_qty: ''}
}
},
methods: {
calculateLineTotal(invoice_product) {
alert(invoice_product.item_qty)
}
},
})
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet" type="text/css">
<link href="https://cdn.jsdelivr.net/npm/quasar#1.19.5/dist/quasar.min.css" rel="stylesheet" type="text/css">
<div id="q-app">
<q-input type="number" min="1" dense borderless debounce="300" class="q-ma-xs" v-
model="invoice_product.item_qty" placeholder="quantity" filled
#keyup="calculateLineTotal(invoice_product)" />
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#^2.0.0/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/quasar#1.19.5/dist/quasar.umd.min.js"></script>

Getting textbox to turn red on change

I'm trying to make my textbox red when someone starts adding text to it. The problem I'm having is it takes long
to hit my onChange method and change the text color red.
Here is my code
<template>
<div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label>Name</label>
<input
type="text"
class="form-control"
v-model="product.name"
#change="onChange"
:style="{ color: conditionalColor}"
>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['product'],
computed: {
conditionalColor(){
return this.dirty ? 'red' : ''
}
},
data() {
return {
dirty: false
}
},
methods: {
onChange(){
console.log('changing');
return this.dirty = true;
}
},
mounted() {
}
}
</script>
Try watching product.name and setting this.dirty = true; there. The first time you edit the input after mounting it will fire and accomplish the same as your onChange method. Additionally, you can save some steps by conditionally applying a CSS class instead of computing it.
<template>
<input type="text" class="form-control" :class="{ 'dirty': dirty }" v-model="product.name">
</template>
<style>
.dirty { color: red; }
</style>
<script>
export default {
props: ['product'],
data() {
return {
dirty: false
}
},
watch: {
'product.name': function() {
this.dirty = true;
}
},
}
</script>
This is also alternative way to style your input.
<template>
<input type="text" class="form-control" :class="{ 'dirty': product.name.length > 0}" v-model="product.name">
</template>
<style>
.dirty { color: red; }
</style>

Trigger submit with prevent default

I'm trying to call submit method on form via refs from my script. On form tag I have #submit.prevent.
<template>
<div id="app">
<form ref="search-form" #submit.prevent="sub">
<input type="text" required />
</form>
<button #click="submitForm">click me</button>
</div>
</template>
<script>
export default {
name: "App",
methods: {
submitForm() {
this.$refs["search-form"].submit();
},
sub() {
console.log("submitted");
},
},
};
</script>
My problems are that when I click button:
page get reloaded like prevent default is not working
sub() is not called at all
Why?
Your question is not clear what you want to do but maybe you need this
const app = new Vue({
methods: {
submitForm() {
this.$refs["search-form"].submit();
},
sub() {
alert('hello');
},
}
})
app.$mount("#app")
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<form ref="search-form" #submit.prevent="sub">
<input type="text" required/>
<button #click.native.prevent="submitForm">Send</button>
</form>
</div>
<template>
<div id="app">
<form ref="search-form">
<input type="text" required />
</form>
<button #click.stop.prevent="processForm">click me</button>
</div>
</template>
<script>
export default {
name: "App",
methods: {
processForm() {
// validation here
this.$refs["search-form"].onsubmit = this.submitForm();
},
submitForm() {
// AJAX here
console.log("submitted");
},
},
};
</script>
⚠️ I assume that when you use prevent means you are going to manually submit the form at a later stage (maybe after client side validation). Then its either you use AJAX or Axios to submit the form to the server side. Calling submit() anywhere here will still reload your page.

#submit.prevent not working on vue md-input

when I click 'login button' even I don't fill data in md-input it still running,
I test my onSubmit() method by login with my user and it works!
I don't think I do thing wrong in the method so I guess that my form is incorrect.
here is my code :
my form
<form #submit.prevent="onSubmit">
<login-card header-color="green">
<h4 slot="title" class="title">CCRP Sign On</h4>
<p slot="description" class="description">IT solution by เจ้เก๋ IT-PM</p>
<md-field class="md-form-group" slot="inputs">
<md-icon>account_box</md-icon>
<label>ID...</label>
<md-input v-model.trim="userId" type="text"></md-input>
</md-field>
<md-field class="md-form-group" slot="inputs">
<md-icon>lock_outline</md-icon>
<label>Password...</label>
<md-input v-model.trim="password" type="password"></md-input>
</md-field>
<md-field class="md-form-group">
<md-icon>announcement</md-icon>
<label>Password...</label>
</md-field>
<md-button slot="footer" class="md-simple md-success md-lg" type="submit">Login</md-button>
</login-card>
</form>
in scrpit methods
async onSubmit() {
const authData = {
userId: this.userId,
password: this.password
};
await this.login(authData).then(() => {
if (this.isAuthenticated) {
this.$router.push("dashboard");
} else {
console.log("err");
}
});
},
can you help me solve this?
Your understanding of "prevent" key is quite incorrect.
All it does is not reload the form after submit action. However the submit action will be called irrespective of whether the prevent is used or not.
It is just preventing the default functionality of form getting reloaded after each submit.
On the other hand what you need to do is validate your form before actually submitting it.
Example :
//- Requires "vuelidate" - npm install vuelidate
<script>
import { validationMixin } from "vuelidate";
import { required, email } from "vuelidate/lib/validators";
export default {
name: "FormValidation",
mixins: [validationMixin],
data: () => ({
form: {
email: null,
password: null
},
userSaved: false,
sending: false,
lastUser: null
}),
validations: {
form: {
email: {
required,
email
},
password: {
required
}
}
},
methods: {
getValidationClass(fieldName) {
const field = this.$v.form[fieldName];
if (field) {
return {
"md-invalid": field.$invalid && field.$dirty
};
}
},
clearForm() {
this.$v.$reset();
this.form.email = null;
this.form.password = null;
},
saveUser() {
this.sending = true;
// Instead of this timeout, here you can call your API
window.setTimeout(() => {
this.lastUser = `${this.form.email}`;
this.userSaved = true;
this.sending = false;
this.clearForm();
}, 1500);
},
validateUser() {
this.$v.$touch();
if (!this.$v.$invalid) {
this.saveUser();
}
}
}
};
</script>
<style lang="scss" scoped>
.md-progress-bar {
position: absolute;
top: 0;
right: 0;
left: 0;
}
</style>
<template>
<div>
<!-- Calling validateUser insted of submit action -->
<form novalidate class="md-layout" #submit.prevent="validateUser">
<md-card class="md-layout-item md-size-50 md-small-size-100">
<!-- Title of the form -->
<md-card-header>
<div class="md-title">Login</div>
</md-card-header>
<!-- Inputs for the form -->
<md-card-content>
<md-field :class="getValidationClass('email')">
<label for="email">Email</label>
<md-input
type="email"
name="email"
id="email"
autocomplete="email"
v-model="form.email"
:disabled="sending"
/>
<span class="md-error" v-if="!$v.form.email.required">The email is required</span>
<span class="md-error" v-else-if="!$v.form.email.email">Invalid email</span>
</md-field>
<md-field :class="getValidationClass('password')">
<label for="password">Password</label>
<md-input
type="password"
name="password"
id="password"
autocomplete="password"
v-model="form.password"
:disabled="sending"
/>
<!-- to show errors in case validation fails -->
<span class="md-error" v-if="!$v.form.password.required">The email is required</span>
<span class="md-error" v-else-if="!$v.form.email.email">Invalid email</span>
</md-field>
</md-card-content>
<md-progress-bar md-mode="indeterminate" v-if="sending"/>
<md-card-actions>
<md-button type="submit" class="md-primary" :disabled="sending">Create user</md-button>
</md-card-actions>
</md-card>
<md-snackbar :md-active.sync="userSaved">The user {{ lastUser }} was saved with success!</md-snackbar>
</form>
</div>
</template>

Vuelidate reset specific field so that $error flag is false

Using Vuelidate you can reset the validation errors by using this.$v.$reset(). In this Codepen example resetting the lastName field that uses a Vuetify component works - $invalid is true while $error is set to false.
When resetting the regular text input for firstName it doesn't work as the $error flag is still true. How can I modify the text input so that $error is false when calling reset?
I've also tried this.$nextTick(() => {...}) but that doesn't work either.
Vue.use(window.vuelidate.default)
var validationMixin = window.vuelidate.validationMixin
const {
maxLength,
required
} = window.validators
new Vue({
el: '#app',
mixins: [validationMixin],
data: () => ({
form: {
firstName: '',
lastName: ''
}
}),
validations: {
form: {
firstName: {
required, maxLength: maxLength(2)
},
lastName: {
required, maxLength: maxLength(2)
},
}
}
})
input.raw {
border: solid;
}
.is-invalid {
border-color: #FF5252 !important;
}
<html>
<head>
<script src="https://unpkg.com/vuelidate#0.6.1/dist/validators.min.js"></script>
<script src="https://unpkg.com/vuelidate#0.6.1/dist/vuelidate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
</head>
<body>
<div id="app">
<label for="firstName">First Name</label>
<input
v-model="form.firstName"
id="firstName"
class="raw"
:class="{ 'is-invalid': $v.form.firstName.$error }"
type="text"
width="100%"
:oninput="$v.form.firstName.$touch()"
:onblur="$v.form.firstName.$touch()"
/>
<button #click="$v.form.firstName.$touch()">
$touch
</button>
<button #click="$v.form.firstName.$reset()">
$reset
</button>
<pre>{{ $v.form.firstName }}</pre>
</div>
</body>
</html>
In your example, you are using oninput and onblur HTML attributes, but in Vue, you should use #input(v-on:input) and #blur(v-on:blur) bindings instead. See docs for details.
Replacing HTML attributes with Vue bindings made your example work correctly:
Vue.use(window.vuelidate.default)
var validationMixin = window.vuelidate.validationMixin
const {
maxLength,
required
} = window.validators
new Vue({
el: '#app',
mixins: [validationMixin],
data: () => ({
form: {
firstName: '',
lastName: ''
}
}),
validations: {
form: {
firstName: {
required, maxLength: maxLength(2)
},
lastName: {
required, maxLength: maxLength(2)
},
}
}
})
input.raw {
border: solid;
}
.is-invalid {
border-color: #FF5252 !important;
}
<html>
<head>
<script src="https://unpkg.com/vuelidate#0.6.1/dist/validators.min.js"></script>
<script src="https://unpkg.com/vuelidate#0.6.1/dist/vuelidate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
</head>
<body>
<div id="app">
<label for="firstName">First Name</label>
<input
v-model="form.firstName"
id="firstName"
class="raw"
:class="{ 'is-invalid': $v.form.firstName.$error }"
type="text"
width="100%"
#input="$v.form.firstName.$touch()"
#blur="$v.form.firstName.$touch()"
/>
<button #click="$v.form.firstName.$touch()">
$touch
</button>
<button #click="$v.form.firstName.$reset()">
$reset
</button>
<pre>{{ $v.form.firstName }}</pre>
</div>
</body>
</html>
This is Issue From Vuelidate and they must be fixed, in this position you can not reset form and give same (badly) behavior you can re-render by the router
// re render component for reset all fileds
this.$router.go(0)