THE SITUATION:
I have many forms in my angular app and i need an effective error messages system. I find ngMessages useful.
But i would like that the error messages appears only when the user blur out of a field, in order to be less 'aggressive'.
ATTEMPT:
Attempt using debouncing at 1000ms
<label class="item item-input">
<span class="input-label"> Email alternative </span>
<input type="email" name="email_alternative" ng-model="profile_edited.email2" ng-model-options="{ debounce: 1000 }">
</label>
<!-- Error messages for ALTERNATIVE EMAIL -->
<label class="item item-input" ng-show="profile_edit_form.email_alternative.$error.email">
<div class="form-errors" ng-messages="profile_edit_form.email_alternative.$error" role="alert" ng-if='profile_edit_form.email_alternative.$dirty'>
<div class="form-error" ng-message="email">Your email address is invalid</div>
</div>
</label>
In this way it will properly appears the error message if the user is not typing anything for one second.
Is the closest i get to what i want.
But sometimes for some users it may take a bit more than 1 second to type the character #. The error message may then suddenly appear and confuse the user.
But if i set the debouncing time 2 or 3 seconds is way too much. It may appear when the user is already writing in another field.
I need that the error messages fire ONLY AFTER the user blur out of the field.
THE QUESTION:
How can i fire ngMessages on blur?
$dirty will evaluate to true if the text is changed in the input box. To check for the blur you can use the $touched property of the input field.
Checkout the forms documentation to see all form (input) properties: https://docs.angularjs.org/guide/forms
You can use $dirty and ng-show to display messages when input is dirty.
<input type="text" name="modelName" ng-model="modelName" required />
<div ng-messages="myForm.modelName.$error" ng-show="myForm.modelName.$dirty">
<div ng-message="required">Please Fill this model.</div>
</div>
Related
Is there a class which I can use for representing !invalid-feedback? What I am trying to do is, I want to have a placeholder under my form input and tell the user that this field needs to only have 3 characters and if the validation fails, I will add a a div with a class of invalid-feedback. I want them to be mutually exclusive so it's either the placeholder or the invalid-feedback.
So for example,
<form:input path="ac" required="required"
class="form-control text-uppercase" maxlength="3" />
<small class="form-text text-muted">Only three characters are allowed</small>
<div class="invalid-feedback">AC is required</div>
in the following block, if the user is either entering the data or if the value is valid, I want to show what's in the <small> tag, if the form is submitted and if there is a validation error, I want to show what's in the invalid-feedback div.
So, is there a class in Bootstrap which is basically !invalid-feedback? Is it valid-feedback? isValid? Can this be done with just using styles and not Javascript/jQuery?
There are Bootstrap classes to indicate a field has an error but since you're asking to display errors in a non-Bootstrap way (i.e. by hiding and showing different tags) you'll have to roll your own validation using Javascript.
Here is one way that monitors the status of the input field and hides/shows the <small> and div.invalid-feedback elements. (Note that this is just a proof-of-concept, and doesn't deal well with all possible ways of the user interacting with the input field.)
$('input').on('input', function() {
if ($(this).val().length == 3) {
$('.invalid-feedback').hide()
$('small').show()
} else {
$('.invalid-feedback').show()
$('small').hide()
}
})
<script src="https://code.jquery.com/jquery-3.6.1.js" integrity="sha256-3zlB5s2uwoUzrXK3BT7AX3FyvojsraNFxCc2vC/7pNI=" crossorigin="anonymous"></script>
<form>
<input path="ac" required="required"
class="form-control text-uppercase" maxlength="3" />
<small class="form-text text-muted">Only three characters are allowed</small>
<div class="invalid-feedback">AC is required</div>
</form>
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
Resolve the conflict between required and my custom validation
causing input fields being prematurely marked as valid
Display b-form-invalid-feedback on datepicker on form submit if
date is not selected
In 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.
I have a big form for a website, with multiple required fields, and all of them are working perfectly, when i click submit on the form, the web page scroll to the field's location with an error message, except on two parts, the "Number of travelers" and the "Date of the trip".
This is the HTML for both of them:
<div class="sect-txt" style="margin-top:100px;" id="op">
<h1> Date of the trip </h1>
<div class="al">
<h1 style="font-family:Montserrat;font-size:14px;color:#161616;margin-bottom:5px;"> Check In </h1>
<input type="date" class="hide-replaced" data-date-size="1" placeholder="Check-in" name="checkin" required />
</div>
<div class="al">
<h1 style="font-family:Montserrat;font-size:14px;color:#161616;margin-bottom:5px;"> Check Out </h1>
<input type="date" class="hide-replaced" data-date-size="1" placeholder="Check-out" name="checkout" required />
</div>
<a href="#four">
<div class="btn-nxt" style="position:relative;top:137px;">
NEXT
</div>
</a>
</div>
<div class="sect-txt">
<h1> Number of travelers </h1>
<input type="number" class="f-2" placeholder="Adults" name="adults" required/>
<input type="number" class="f-3" placeholder="Children" name="childrens" required/>
<a href="#fif">
<div class="btn-nxt-b">
NEXT
</div>
</a>
</div>
And this is a link to the page in action: http://www.eliteware.co/92/form/
Your button is not focusable because you are trying to hide it when it has to receive focus again. Check the following link for more information about why this happens. Basically, you are hiding the object that is supposed to receive focus when validation is needed. If you don't want this to happen, you can probably do validation before hiding, or unhide the object if validation fails.
https://stackoverflow.com/a/28340579/616813
Also, do remember, if an error log exists, that is the first point to check if you receive an error. That is the whole point of error log, to give you a starting point to debug.
Or as Andreas said, "Fix the damn errors in the console... :)".
Edit:
Because it was killing me, I tried to reverse engineer your application. All it took was comparing the textbox that was working, and the one that was failing to find the problem. Really, that easy.
aria-required="true"
Your "Adults" and "Children" input fields have this property. You need required="true" instead.
Check your css and update that. And no, I have no idea why "aria=required" and "required" property behave differently. It is something new to learn for sure.
I have a form where i am showing validation error below each input field.
<div class="form-group m-0">
<input type="text" class="form-control" name="address" placeholder="Address">
<i class="form-group__bar error-red address"></i>
</div>
when Ajax response have errors regarding to the input field, I shows them here <i class="form-group__bar error-red address"></i>.
What i want if that when the error shows and the user clicked on the input field i want to disappear the error message.
Is there any library for this kinda things, Cause there are many fields in the form, It will be good where we can use library for every form and field.
you can use .focusin() function. What you have to do is just give common class to each <i class="form-group__bar error-red address"></i>.
like if your common class for each <i class="form-group__bar error-red address"></i> is "form-group__bar" then you can use focusin like this.
$(".form-group input").focusin(function() {
$(this).siblings(".form-group__bar").hide()
});
this will hide error message on focus of input.
For more you can search for focusin and focusout events.
use this code no library needed just jquery which you already use
$("input").focus(function(){
$(this).siblings("i.error-red").hide();
});
I have Custom input component with validation with ngMessages,FormController and ng-required:
<div class="col-sm-9 col-xs-12">
<input
id="{{$ctrl.fieldName}}"
name="{{$ctrl.fieldName}}"
class="form-control"
type="text"
minlength="{{$ctrl.minLength}}"
maxlength="{{$ctrl.maxLength}}"
ng-required="{{$ctrl.isRequired === 'true'}}"
ng-model="$ctrl.value"
ng-change="$ctrl.form.$submitted = false;"
>
<div
ng-messages="$ctrl.form[$ctrl.fieldName].$error"
ng-if="$ctrl.form.$submitted"
>
<span class="help-block" ng-message="required">
Field is required
</span>
<span class="help-block" ng-message="minlength">
Minimum length of field: {{$ctrl.minLength}}
</span>
<span class="help-block" ng-message="maxlength">
Maximum length of field: {{$ctrl.maxLength}}
</span>
</div>
</div>
Which is used in this way:
<act-text-field
form="heroAddForm"
field-name="name"
min-length="3"
max-length="15"
is-required="true"
errors="$ctrl.errors.name"
ng-model="$ctrl.hero.name">
</act-text-field>
What I want to achieve is validation fires when user clicks submit button. And it works, validation fires also for required field name, but also for field description which is not required:
<act-text-field
form="heroAddForm"
field-name="description"
max-length="50"
is-required="false"
errors="$ctrl.errors.description"
ng-model="$ctrl.hero.description"
></act-text-field>
Also for this field validation messages are visible, although field description is valid, cause I add class has-error to invalid fields:
<div class="form-group"
ng-class="{'has-error': $ctrl.form.$submitted && (!$ctrl.form[$ctrl.fieldName].$valid)}"
>
<!-- rest of code -->
You can easily reproduced this wrong behaviour in my Plunker: Custom input demo app with validation states (I know it has other mistakes). I think ng-message="required" should not be visible, because field description is not required. I know I can add some ng-ifs to code to by-pass it, but I think I make a mistake somewhere which I can't see. Do you see where I made a mistake? Thank you in advance for every help.
I found a solution, again I forgot to include ngMessages. Without it, my code went crazy, I apologize for wasting your time :)