I'm trying to imitate Bootstrap form validation styling with Vue and Vee-validate.
In order to have that Boostrap validation error message, when there's a validation error, the input itself must have is-invalid class presents. And in addition, the error message element must have invalid-feedback class, of course.
I'm struggling to add is-invalid class to the input when there's a validation error.
In Vee-validate 3, I was able to control the input element's classes with this guide. But it seems to be deprecated.
This is a code sandbox that you can play with. Nothing extra-ordinary, just straight out of Veevalidate example.
<template>
<div id="app">
<Form #submit="onSubmit">
<Field name="email" type="email" :rules="validateEmail" class="form-control"/>
<ErrorMessage class="invalid-feedback" name="email" />
<button class="btn btn-primary">Sign up</button>
</Form>
</div>
</template>
<script>
import {
Form,
Field,
ErrorMessage
} from "vee-validate";
export default {
components: {
Form,
Field,
ErrorMessage,
},
methods: {
onSubmit(values) {
console.log(values, null, 2);
},
validateEmail(value) {
// if the field is empty
if (!value) {
return "This field is required";
}
// if the field is not a valid email
const regex = /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i;
if (!regex.test(value)) {
return "This field must be a valid email";
}
// All is good
return true;
},
},
};
</script>
<style>
span {
display: block;
margin: 10px 0;
}
</style>
Versions
"vee-validate": "^4.5.11",
"vue": "^3.2.33",
You can render more complex fields, by utilizing the scoped slots of the <Field />-component.
If you replace your Field-component with the following, it should work as expected:
<Field name="email" :rules="validateEmail" v-slot="{ field, errors }">
<input v-bind="field" type="email" :class="{'is-invalid': !!errors.length }" />
</Field>
Related
I am trying to capture errors in forms and have to display them in modal. But vee-validate onInvaidSubmit seems not working. See code below.
PS: I copied this example from https://vee-validate.logaretm.com/v4/guide/components/handling-forms
Vue Version : 3, vee-validate: 4.5.11
<template>
<Form v-slot="{ validate }" :validation-schema="schema">
<Field name="email" type="email" />
<ErrorMessage name="email" />
<Field name="password" type="password" />
<ErrorMessage name="password" />
<button #click="validate">Submit</button>
</Form>
</template>
<script>
import { Form, Field, ErrorMessage } from 'vee-validate'
import * as yup from 'yup'
export default {
components: {
Form,
Field,
ErrorMessage,
},
data() {
const schema = yup.object({
email: yup.string().required().email(),
password: yup.string().required().min(8),
})
return {
schema,
}
},
methods: {
onSubmit(values) {
// Submit values to API...
alert(JSON.stringify(values, null, 2))
},
onInvalidSubmit({ values, errors, results }) {
console.log(values) // current form values
console.log(errors) // a map of field names and their first error message
console.log(results) // a detailed map of field names and their validation results
},
},
}
</script>
try the following by adding it to the Form element. see below
<Form v-slot="{ validate }" :validation-schema="schema" #invalid-submit="onInvalidSubmit">
<Field name="email" type="email" />
<ErrorMessage name="email" />
<Field name="password" type="password" />
<ErrorMessage name="password" />
<button #click="validate">Submit</button>
</Form>
<div class="row">
<ValidationProvider v-slot="{errors, valid}" rules="required" class="col-md-6">
<b-field :label="$t('admin_global_username')" :type="{'is-success':valid, 'is-danger': errors[0]}" :message="errors && $t(errors[0])">
<b-input v-model="employeeModal.modalData.username" type="text" />
</b-field>
</ValidationProvider>
</div>
I am using Vee-validate and i want this : İf b-field :type in is-danger mode. user can't go other page. how can i know what is the type in script tags ?
You could add a template ref to <b-field>, and check its type prop directly in the form's submit-event handler:
<b-field ref="myField" />
export default {
methods: {
submit() {
if (this.$refs.myField.type === 'is-danger') {
alert('you shall not pass!')
} else {
console.log('submitting')
}
}
}
}
demo
My form has a first name and last name that initially appear as one compact string, but split into two input fields when the user clicks on the string. I need these two input fields to disappear and revert back to the compact string format when the user clicks elsewhere on the page (anywhere other than inside those two input fields). In order to achieve this, I use a v-on directive with the blur attribute. This works well, but only if the user first clicks into one of the input fields to trigger focus. This is because blur only triggers on an input field that is already in focus. Is there either a way for me to automatically put the first input field in focus when the input element conditionally renders? Is there a better approach?
Cheers.
Here is my HTML:
<a v-if="!eitherNameSelected" #click="firstNameSelected">
{{person.firstname}} {{person.lastname}}</a>
<div class="row" v-else-if="eitherNameSelected" v-enter="focusOnFirstChild($event)">
<div class="col-md-3">
<input
#focus="firstnameselected=true"
#blur="firstnameselected=false"
type="text"
class="form-control"
name="example-text-input-invalid is-invalid"
id="firstname"
placeholder="First Name"
v-model="person.firstname"
>
<div class="invalid-feedback">
Invalid feedback
</div>
</div>
<div class="col-md-3">
<input
#focus="lastnameselected=true"
#blur="lastnameselected=false"
type="text"
class="form-control"
name="example-text-input-invalid is-invalid"
placeholder="Last Name"
v-model="person.lastname"
>
<div class="invalid-feedback">
Invalid feedback
</div>
And in my Javascript ...
data() {
return {
listid: 0,
listname: "",
personid: 0,
person: {},
nameselected: false,
activetab1: "main",
activetab2: "notes",
firstnameselected: false,
lastnameselected: false,
....
methods: {
firstNameSelected() {
var elem = vm.$el.getElementById('firstname');
elem.focus();
this.firstnameselected="true";
}
},
computed: {
eitherNameSelected() {
return (this.firstnameselected || this.lastnameselected);
}
},
It may not be the best solution but you could use a #click($event) in order to know where you clicked to hide or not the inputs.
This post about checking event targets can be a good start I guess.
I also coded a minimal example to help you get through your issue. I hope it will help you.
new Vue({
el: "#app",
data: {
person: {
firstname: 'PersonFirstname',
lastname: 'PersonLastname'
},
showCompactString: true
},
methods: {
onCompactStringClicked() {
this.showCompactString = false
},
// Hide the inputs if a click is triggered outside of them
onAppClicked(event) {
// Do nothing if compact string is being shown
if (this.showCompactString)
return
const fistnameInputClickedOn = event.target.matches('#firstname')
const lastnameInputClickedOn = event.target.matches('#lastname')
const anyInputClickedOn = fistnameInputClickedOn || lastnameInputClickedOn
if (!anyInputClickedOn)
this.showCompactString = true
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" #click="onAppClicked($event)">
<a v-if="showCompactString" #click.stop="onCompactStringClicked">{{person.firstname}} {{person.lastname}}</a>
<div class="row" v-else>
<div>
<input type="text" id="firstname" placeholder="First Name" v-model="person.firstname">
</div>
<div>
<input type="text" id="lastname" placeholder="Last Name" v-model="person.lastname">
</div>
</div>
</div>
Don't hesitate to leave a comment if you have trouble. I'll answer you back as fast as I can.
put an #click=someFunction (or computed) on the root element (i.e. the whole body of HTML) and then have that change the text.
About the issue
I am using Laravel 5.6.7 with vue.js. vee-validate is being used for validation
When the form loads, it shows validation error messages. User did not even click the submit button. Below is the screenshot.
Code
<template>
<div>
<form role="form">
<input v-validate data-vv-rules="required" type="text"
v-model="UpdateForm.First_Name">
<p v-if="errors.has('First Name')">{{ errors.first('First Name') }}</p>
<button type="button">
Update Profile
</button>
</form>
</div>
</template>
<script>
export default {
data() {
return {
UpdateForm: {
First_Name: ''
}
}
},
created() {
this.GetProfile();
},
methods: {
GetProfile() {
axios.post("some api url", {}).then(response => {
this.UpdateForm.First_Name = response.data.Data.First_Name;
});
}
}
}
</script>
Could I get rid of validation error messages on form load?
This is not the expected behavior. For initial validating you need to inform it with v-validate.initial.
Maybe you are defining this to happen when declaring v-validate or in other place.
Vue.use(VeeValidate);
new Vue({
el: '#demo'
})
.is-danger{
color: red;
}
<script src="https://unpkg.com/vue"></script>
<script src="https://cdn.jsdelivr.net/npm/vee-validate#latest/dist/vee-validate.js"></script>
<div id="demo">
<label>This one needs touching</label>
<input type="text" name="name" v-validate="'required'">
<div v-show="errors.has('name')" class="is-danger">Errors: {{ errors.first('name') }}</div>
<br/>
<label>This one does not need touching</label>
<input name="name2" v-validate.initial="'required'" type="text">
<div v-show="errors.has('name2')" class="is-danger">{{ errors.first('name2') }}</div>
</div>
Changed
this.editForm.First_Name = Data.User.First_Name;
to
if(Data.User.First_Name != null && Data.User.First_Name != "") {
this.editForm.First_Name = Data.User.First_Name;
}
and validation is working fine now. Basically the variable is not initialized.
Vue.js 2 - I am trying to bind form inputs but I always get the erro message ( on all inputs ..)
v-on:model="form.email" expects a function value, got undefined
<form id="registrationForm">
<div class="form-group">
<input type="email" name="email" id="email" #model="form.email" placeholder="enter your email address">
</div>
<button #click="sendRegistration" type="submit" class="btn btn-primary btn-gradient submit">SEND</button>
</form>
and the script
data: function() {
return {
form: {
...
email: '',
...
}
}
},
methods: {
sendRegistration: function() {
console.log('sending form')
return false
}
},
You're getting some things mixed up. Attributes starting with v-on:, often abbreviated as #, are used to register event listeners on elements. #click="sendRegistration" will for example register the sendRegistration method defined on your Vue instance as a handler for that element's click event.
What you're trying to accomplish has nothing to do with event handling. The attribute you need is called v-model and binds an <input>'s value to a value saved on your Vue instance.
<input type="email" name="email" id="email" #model="form.email">
should be
<input type="email" name="email" id="email" v-model="form.email">