I am using Vue.js and Bootstrap to design a website. I have a form that I am trying to run my custom validation on. It works fine until the user clicks submit which adds the was-validated
class to the form per the bootstrap documentation.
At this point any required input field that has any input whether it meets my custom validation or not is marked as valid and gets a green border and check mark. My custom validation is still being run and displaying b-form-invalid-feedback
correctly. However, it seems that was-validated
is marking fields with the required prop as valid while not taking my custom validation into account this is leading to conflicting validation as a field has a green check mark (because it satisfies the required property) but still an error message because it is not yet valid per my custom validation.
I have tried removing the :valid
style this isn't the effect I want as I do want it to display those styles when it is valid per my validation. Hope this makes sense if not I will provide pictures. I also have a second issue I have a date picker that is not displaying b-form-invalid-feedback
at all even when was-validated
is added.
My Code
<b-form @submit.prevent="addReview" name="review-form" novalidate>
<div class="name">
<label class="sr-only" for="form-input-name">Name</label>
<b-input id="form-input-name" class="form-inputs mb-2 mr-sm-2 mb-sm-0" v-model="name" placeholder="Name" required :state="isStateValid(this.name)"></b-input>
<b-form-invalid-feedback id="form-input-name">
You must enter a name
</b-form-invalid-feedback>
</div>
<div class="date">
<label class="sr-only" for="example-datepicker">Choose a date</label>
<b-form-datepicker id="datepicker" v-model="dateVisited" class="mb-2" required placeholder="Date Visited" :state="isStateValid(this.dateVisited)"></b-form-datepicker>
<b-form-invalid-feedback id="datepicker">
You must enter a valid date
</b-form-invalid-feedback>
</div>
<div class="service">
<label class="sr-only" for="form-input-service">Service Provided</label>
<b-input id="form-input-service" class="form-inputs mb-2" placeholder="Service Provided" v-model="service" required :state="isStateValid(this.service)"></b-input>
<b-form-invalid-feedback id="form-input-service">
You must enter the service provided
</b-form-invalid-feedback>
</div>
<div class="email">
<label class="sr-only" for="inline-form-input-username">Email</label>
<b-input id="inline-form-input-username" class="form-control mb-2 mr-sm-2 mb-sm-0" placeholder="Email" v-model="email" required :state="emailStateValidation"></b-input>
<b-form-invalid-feedback id="inline-form-input-username">
You must enter the part of your email that comes before the '@' symbol
</b-form-invalid-feedback>
</div>
<div class="domain">
<label class="sr-only" for="inline-form-input-domain">Domain</label>
<b-input-group prepend="@" class="mb-2 mr-sm-2 mb-sm-0">
<b-input id="inline-form-input-domain" placeholder="Domain ex: gmail.com" v-model="domain" required :state="domainStateValidation"></b-input>
<b-form-invalid-feedback id="inline-form-input-domain">
You must enter the part of your email that comes after the '@' symbol
</b-form-invalid-feedback>
</b-input-group>
</div>
<div class="description">
<label class="sr-only" for="textarea-rows">Describe Your Experience</label>
<b-form-textarea class="mb-3 mr-sm-2 mb-sm-0" id="textarea-rows" placeholder="Describe Your Experience" rows="4" required v-model="description" :state="isStateValid(this.description)"></b-form-textarea>
<b-form-invalid-feedback id="textarea-rows">
You must enter a description of your experience
</b-form-invalid-feedback>
</div>
<b-button type="submit">Save</b-button>
</b-form>
computed: {
emailStateValidation() {
if (this.email) {
return this.emailIsValid() ? true : false;
}
return null;
},
domainStateValidation() {
if (this.domain) {
return this.domainIsValid() ? true : false;
}
return null;
},
},
methods: {
emailIsValid() {
let regEx = /^(?!.*@)((^[^\.])[a-z0-9\.!#$%&'*+\-\/=?^_`{|}~"]*)*([^\.]$)/;
return regEx.test(this.email);
},
domainIsValid() {
let regEx = /((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return regEx.test(this.domain);
},
isStateValid(variable) {
if (variable) {
return variable.length > 0 ? true : false;
}
return null;
},
addReview() {
let mainForm = document.getElementsByName("review-form")[0];
mainForm.classList.add("was-validated");
...
Questions
b-form-invalid-feedback
on datepicker on form submit if
date is not selectedIn a nutshell, remove novalidate
from <form>
in your Vue template. When you set novalidate
, the inputs will remain in their :valid
state throughout their lifecycle until you explicitly call setCustomValidity
. Working Sandbox
Since, Bootstrap styles also apply to :valid
or :invalid
states so, even if your custom validators determine inputs to be invalid, both valid and invalid styles will get applied i.e. :valid
and .is-invalid
but, I guess it's just happens so, that :valid
styles take precedence the way Bootstrap stylesheet is currently written.
U̶s̶e̶ ̶̶n̶o̶v̶a̶l̶i̶d̶a̶t̶e̶
̶ ̶w̶h̶e̶n̶ ̶y̶o̶u̶'̶r̶e̶ ̶i̶m̶p̶l̶e̶m̶e̶n̶t̶i̶n̶g̶ ̶a̶ ̶c̶o̶m̶p̶l̶e̶t̶e̶ ̶v̶a̶l̶i̶d̶a̶t̶i̶o̶n̶ ̶s̶o̶l̶u̶t̶i̶o̶n̶ ̶y̶o̶u̶r̶s̶e̶l̶f̶ ̶i̶n̶c̶l̶u̶d̶i̶n̶g̶ ̶̶r̶e̶q̶u̶i̶r̶e̶d̶
̶ ̶v̶a̶l̶i̶d̶a̶t̶o̶r̶.̶
With Bootstrap, since it also applies styles to :valid
or :invalid
states of input, you're better off NOT using novalidate
.
Ofcourse, this will enable browser popups asking for filling certain fields which might be unwanted.
Suggestion: Use validated
prop on your form and bind it to your form's state and set it to true
in addReview()
, it will automatically add was-validated
class and you don't need to manipulate the DOM directly.
EDIT: Since removing novalidate
enables browser validation, submit
event no longer fired on the form and hence, was-validated
class is never added to the form. This presented an issue in my original answer because messages and icon were not shown without was-validated
. I have modified the sandbox to suggest a fix for that and that is to bind click
event to submit button for validation logic and using submit
event for stuff that should happen after successful validation.
EDIT for Datepicker: The reason why datepicker never invalidated was because of an issue in isStateValid()
method specifically the part:
if(variable) { // "" evaluates to false
// ...
}
Since ""
evaluates to false, it will always return null
. The fix for that is in combination with the suggestion above of maintaining validated
state for the form. Now, instead of checking if(variable)
, we check if(this.validated)
and if it is true
, we simply check the length and return either true
or false
.
Fundamentally was-validated
is not bootstrap-vue native, it's browser native, which also has no understanding of :state
. If you want to use was-validated
you can't use custom validations. If you want to use custom validations. See the suggestion For 2. Which is basically, use another variable to control whether validation should be applied.
From the documentation on bootstrap-vue
When set, adds the
aria-required="true"
attribute on the component. Required validation needs to be handled by your application
You need to explicitly check that the validation should show, it isn't clear from the documentation what required
actually does, but it doesn't affect validation. Which explains why that part isn't working. Personally I set a global this.showValidations = true
on submit, so that the validations actually run at the right time and not before (and after when expected). In your case, you can check for the was-validated
class that you are adding explicitly. It isn't great, but it seems it must be done here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With