Bootstrap 4 form validation with Vanilla JS malfunction - javascript

I was able to create a script to validate my Bootstrap 4 form but somehow the error message is REPLACING the input field. Is there an elegant way to validate a BS4 with Vanilla JS or I should just go down the road of using Bootstrap validation? What's the best practice in the industry? It's my first time dealing with form validation.
Here's the fiddle:
https://jsfiddle.net/wunrsjdy/
HTML:
<div class="container">
<div id="form">
<h1 class="page-title">Quer Ser Nosso Cliente? Preencha o QuestionĂ¡rio Abaixo</h1>
<form id="form-user" action ="#" method="POST">
<div class="form-group" id='errorTeste'>
<label for="name">Empresa</label>
<input type="text" class="form-control" id="name" name= "name" placeholder="">
</div>
<button type="submit" class="btn btn-primary" id="button-send">Enviar</button>
</form>
</div>
JS
const name = document.getElementById('name')
const form = document.getElementById('form-user')
const errorElement = document.getElementById('errorTeste')
form.addEventListener('submit', (e) => {
let messages = []
if (name.value === '' || name.value == null) {
messages.push('Preencha o nome')
}
if (messages.length > 0) {
e.preventDefault()
errorElement.innerText = messages.join(', ')
}
})

In your fiddle, you are changing the innerText attribute of errorElement. But errorElement is also (equal to) your .form-group element:
const errorElement = document.getElementById('errorTeste')
// A little further down your html...
<div class="form-group" id='errorTeste'>
I believe that this causes your input and label elements in .form-group to be replaced by the inner text.
I feel you are on the right track, though. Try showing another element that contains a styled error/warning message when a user input is invalid using the element's style.display attribute. Maybe you tried giving the #errorTeste id to a separate error message element, but assigned it to the .form-group element by accident? Just a hunch.
Here's a decent example of how you could make it look:

Related

Tracking validated submissions using GTM on Mailchimp Embedded Form

I'm using GTM to track submissions to an embedded Mailchimp form. Relevant post here: Tracking submissions on MailChimp embedded form
Per the original post answer, I am able to use this code to track form submissions.
$('form#mc-embedded-subscribe-form').submit(function(e) {
dataLayer.push({'event':'formSubmit'});
return true;
});
But right now, all clicks of the submit button are being tracked as form submissions, even if the form is not submitted. The answer included a tip to add e.preventDefault(); to prevent false form submissions from being tracked. Could someone tell me where I need to add preventDefault(), or if there's another method, how I can prevent false form submissions from being tracked.
I have tried inserting preventDefault() a number of places in the code, and have not gotten the desired result.
<!-- Begin Mailchimp Signup Form -->
<link href="//cdn-images.mailchimp.com/embedcode/classic-10_7.css" rel="stylesheet" type="text/css">
<style type="text/css">
#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; }
/* Add your own Mailchimp form style overrides in your site stylesheet or in this style block.
We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */
</style>
<div id="mc_embed_signup">
<form action="https://..." method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
<div id="mc_embed_signup_scroll">
<h2>Subscribe</h2>
<div class="indicates-required"><span class="asterisk">*</span> indicates required</div>
<div class="mc-field-group">
<label for="mce-EMAIL">Email Address <span class="asterisk">*</span>
</label>
<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
</div>
<div class="mc-field-group">
<label for="mce-FNAME">First Name </label>
<input type="text" value="" name="FNAME" class="" id="mce-FNAME">
</div>
<div class="mc-field-group">
<label for="mce-LNAME">Last Name </label>
<input type="text" value="" name="LNAME" class="" id="mce-LNAME">
</div>
<div id="mce-responses" class="clear">
<div class="response" id="mce-error-response" style="display:none"></div>
<div class="response" id="mce-success-response" style="display:none"></div>
</div> <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
<div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_c46d540e26068777472a049e9_3aa4dd9218" tabindex="-1" value=""></div>
<div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
</div>
</form>
</div>
<script type='text/javascript' src='//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js'></script><script type='text/javascript'>(function($) {window.fnames = new Array(); window.ftypes = new Array();fnames[0]='EMAIL';ftypes[0]='email';fnames[1]='FNAME';ftypes[1]='text';fnames[2]='LNAME';ftypes[2]='text';...fnames[13]='PAGEURL';ftypes[13]='text';}(jQuery));var $mcj = jQuery.noConflict(true);$('form#mc-embedded-subscribe-form').submit(function(e){dataLayer.push({'event':'formSubmit'});return true;});</script>
<!--End mc_embed_signup-->
The e.preventDefault() function only stops the form from submitting, it does nothing to check for validation and without having anything after it, nothing will happen.
The e.preventDefault() solution mentioned helps you to stop the trigger from being run, and then allows you to validate the form inputs before you actually trigger the event in Google Tag Manager.
The validation of inputs depend on what inputs are required in your form, so this will have to vary based on each form you create. But you could use a generic function that checks all inputs with class 'required', like so:
// Get all required inputs
var requiredFields = document.querySelectorAll('input.required');
// Create function to validate inputs
function validateInputs(callback){
// Set formstatus to valid
var formOkay = true;
// Check each field
requiredFields.forEach(i => {
// Check error state
if ((i.value == "") || ((i.type === 'checkbox') && (i.checked == false))) {
// Form was invalid
console.log('Form was invalid');
formOkay = false;
}
})
// Return formStatus;
callback(formOK ? true : false);
}
Now you can use this function to validate the form, before you send the Google Tag Manager trigger, like so:
document.getElementById('mc-embedded-subscribe-form').addEventListener('submit', e => {
e.preventDefault();
validateInputs( res => {
// check res
if (res == true){
dataLayer.push({'event':'formSubmit'});
}
})
})
Note:
This solution will check that the form inputs are valid, and not if the user was actually subscribed to the list. The best approach would be to subscribe the user, using ajax and then trigger the GTM-tag if the callback returns with status success.
I used a mutation observer to listen for changes to the div with the success message, which then could push an event to the dataLayer
// The element with success message
const successElement = document.getElementById('mce-success-response');
if(successElement){
const mutationConfig = { attributes: true };
const callback = function(mutationsList, observer) {
for(const mutation of mutationsList) {
if (mutation.type === 'attributes'
&& mutation.attributeName == 'style'
&& successElement.style.display === '') {
window.dataLayer.push({
"event" : "my-super-hot-lead"
})
}
}
};
const observer = new MutationObserver(callback);
observer.observe(successElement, mutationConfig);
}

Form Validation Not Resetting After Failing Validation

I'm using a small script to validate a postcode, which works and stops the user entering an invalid password, but when an invalid post code is entered you then can't submit a correct entry. For example, if I enter 'ST' I get the message telling me the postcode is invalid, so without refreshing the page manually I enter 'ST6 1SA' (which is a valid Stoke postcode) and I can't submit the form, I just keep getting the invalid tool tip advising me the post code is not in the correct format.
JS:
<script>
// Validate the postcode before it's sent
(function () {
var postcode = document.getElementById('postcode-entry');
var wrapper = document.getElementById('validation');
var notify = document.createElement('div');
var mnisLookup = document.getElementById('mnis-results');
var matchingClients = document.getElementById('matching-clients');
var postcodeWrapper = document.getElementById('postcode-wrapper');
notify.id = 'notify';
notify.style.display = 'none';
wrapper.appendChild(notify);
postcode.addEventListener('invalid', function (event) {
if (!event.target.validity.valid) {
notify.textContent = 'Please enter a valid postcode e.g. ST1, ST1 4BJ';
notify.className = 'error';
notify.style.display = 'block';
postcode.className = 'form-control invalid';
}
});
})();
</script>
HTML:
<form id="postcode-wrapper" class="form-horizontal">
<div id="postcode-lookup" class="form-group">
<label for="postcode-entry" class="col-sm-1">Postcode:</label>
<div id="postcode-entry-wrapper" class="col-sm-3">
<input type="text" pattern="^(([gG][iI][rR] {0,}0[aA]{2})|((([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y]?[0-9][0-9]?)|(([a-pr-uwyzA-PR-UWYZ][0-9][a-hjkstuwA-HJKSTUW])|([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y][0-9][abehmnprv-yABEHMNPRV-Y])))( {0,}[0-9][abd-hjlnp-uw-zABD-HJLNP-UW-Z]{2})?))$" oninvalid="setCustomValidity('Invalid Post Code Format ')" class="form-control" id="postcode-entry" placeholder="Enter your postcode" name="Postcode" />
</div>
<div class="col-sm-1">
<input id="search" type="submit" value="Search" class="btn btn-default" />
</div>
<div id="validation" class="col-sm-7"></div>
</div>
</form>
Just a quick note that may affect how the page is refreshing, this is inside an MVC Razor page and wrapped with Html.BeginForm - not sure if that makes a difference?
While debugging your code, i found that the event.target.validity.valid was returning false even if the input was valid e.g. 'ST6 1SA'. This was occuring because it does not update the custom validation for the new input and the previous state persists even after entering the valid input.
So to update and reset the previous validation, you have to reset setCustomValidity('') on input change, i.e. oninput="setCustomValidity('')"
Please replace this code:
<input type="text" pattern="^(([gG][iI][rR] {0,}0[aA]{2})|((([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y]?[0-9][0-9]?)|(([a-pr-uwyzA-PR-UWYZ][0-9][a-hjkstuwA-HJKSTUW])|([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y][0-9][abehmnprv-yABEHMNPRV-Y])))( {0,}[0-9][abd-hjlnp-uw-zABD-HJLNP-UW-Z]{2})?))$" oninvalid="setCustomValidity('Invalid Post Code Format ')" class="form-control" id="postcode-entry" placeholder="Enter your postcode" name="Postcode" oninput="setCustomValidity('')"/>

AngularJS - form validation, v 1.4.8

This is my first time using AngularJS, and the form validation is making me question my sanity. You would think this would be the easy part, but no matter how many ways I've tried Googling, the only thing that works is if I set a flag inside my controller's submit if the form is invalid to set the error class. I've looked at similar problems here, but none of them helped, so please do not simply dismiss this as a potential duplicate. Everything else has been a fail.
In the example mark up below I have reduced my form down to just one element. Here is what I have observed:
Using only $error.required does work. The ng-class { 'has-error' :registerForm.firstName.$error.required} does outline the text box with the bootstrap has-ertror class, but this is on form load, which I do not want.
The <p> element with the error message will exhibit the same behavior, so I know that the message exists and is not malfored. It will also display if I only use $error.required. But as soon as I add && registerForm.$submitted ( or $isdirty or !notpristine ) the message will not display on form submit. There are no errors (have developers tools open in chrome) and will post to the web API with no problem and return ok 200 or 400 if I send bad params.
I can write validation code inside my controller, checking if the field has a value and setting a flag on $scope such as $scope.firstNameIsRequired and that will work fine setting ng-show="$scope.firstNameIsRequired", but that will remove testability.
So the problem definitely has to be with how I am adding this in the markup. But after a weekend spent googling I am at my wits end. The only other thing different is that I am using a span on a click element to submit the form instead of an input = submit, but the registerForm.$valid function is setting the correct value. Do I somehow need to trigger the form validation in that ng-click directive?
I am using angular.js v 1.4.8.
I do have angular ui which has it's own validate, but that shouldn't interfere with the basic validation.
Here is the simplified markup:
<form name="registerForm" class="form-group form-group-sm"
ng-controller="userAccountController" novalidate>
<div class="form-group"
ng-class="{ 'has-error' : registerForm.firstName.$error.required }">
<div><label>First Name</label> </div>
<input type="text" class="form-control" id="firstName" name="firstName" value=""
ng-model="firstName" placeholder="First Name" maxlength="100" required=""/>
<p ng-show="registerForm.firstName.$error.required && registerForm.$submitted"
class="alert alert-danger">First Name is required</p>
</div>
<div>
<span class="btn btn-default"
ng-click="submit(registerForm.$valid)">Register</span>
</div>
My controller code is
angular.module( "Application" ).controller( "userAccountController", [
"$scope", "userAccountService", function ( $scope, userAccountService)
{
$scope.hasErrors = false;
$scope.errorMessages = "";
$scope.emailExists = true;
$scope.clearErrors = function (){
$scope.hasErrors = false;
}
$scope.onSuccess = function ( response ) {
alert( "succeeded" );
}
$scope.submit = function (isValid) {
if ($scope.registerForm.$invalid)
return;
alert("isvalid");
$scope.clearErrors();
var userProfile = $scope.createUser();
userAccountService.registerUser(userProfile, $scope.onSuccess, $scope.onError);
}
$scope.createUser = function () {
return {
FirstName: $scope.firstName, LastName: $scope.lastName, Email: $scope.email,
Password: $scope.password, SendAlerts: $scope.sendAlerts
};
};
}
]);
Any help will be appreciated. I probably just need a second set of eyes here because I have been dealing with this on and off since late Friday.
in angular you want use the element.$valid to check wheter an model is valid or not - and you use element.$error.{type} to check for a specific validation error.
Keep in mind that the form.$submitted will only be set if the form is actually submitted - and if it has validationerrors it will not be submitted (and thus that flag is still false)
If you want to show errors only on submit you could use a button with type="submit" and bind to ng-click event - and use that to set a flag that the form has been validated. And handling the submit if the form is valid.
A short example with 2 textboxes, having required and minlength validation:
angular.module("myApp", [])
.controller("myFormController", function($scope) {
$scope.isValidated = false;
$scope.submit = function(myForm) {
$scope.isValidated = true;
if(myForm.$valid) {
console.log("SUCCESS!!");
}
};
});
.form-group {
margin: 10px;
padding: 10px;
}
.form-group.has-error {
border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
<div ng-app="myApp" ng-controller="myFormController">
<form name="myForm">
<div class="form-group" ng-class="{'has-error': myForm.name.$invalid && isValidated}">
<span>Name:</span>
<input type="text" name="name" minlength="5" ng-model="name" required />
<span ng-if="myForm.name.$error.required && isValidated">Name is required</span>
<span ng-if="myForm.name.$error.minlength && isValidated">Length must be atleast 5 characters</span>
</div>
<div class="form-group" ng-class="{'has-error': myForm.email.$invalid && isValidated}">
<span>Email:</span>
<input type="text" name="email" minlength="5" ng-model="email" required />
<span ng-if="myForm.email.$error.required && isValidated">Email is required</span>
<span ng-if="myForm.email.$error.minlength && isValidated">Length must be atleast 5 characters</span>
</div>
<button type="submit" ng-click="submit(myForm)">Submit</button>
</form>
</div>

How to perform validation in multiple groups of inputs according to condition

I am creating a set of textboxes dynamically while pressing (+) button, by cloning the following HTML template:
<div id= "other_leaders" class="controls form-input">
<input type="text" name="other_leader_fname[]" class="input_bottom other_leader_fname" id="other_leader_fname" placeholder="First Name" value="'.$val[0].'" />
<input type="text" name="other_leader_lname[]" class="input_bottom other_leader_lname" id="other_leader_lname" placeholder="Last Name" value="'.$val[1].'" />
<input type="text" name="other_leader_email[]" class="other_leader_email" id="other_leader_email" placeholder="Email Address" value="'.$val[2].'" />
<input type="text" name="other_leader_org[]" class="other_leader_org" id="other_leader_org" placeholder="Organisation/College" value="'.$val[3].'" />
<span class="remove btn"><i class="icon-minus"></i></span>
</div>
I am able to do single textbox validation by following code:
$("input[name*='other_leader_fname']").each(function(){
if($(this).val()=="" || !RegExpression.test($(this).val()))
{
$(this).addClass('custom-error')
fnameflag = 0;
}
});
Now my question is how to do empty validation for all four textboxes, if any one textbox field is filled by the user in that particular textbox group.
for example: if i enter values in the <div> with id other_leader_fname, then it should perform empty validation for other three textboxes of this particular group.
how can i do it?
Try this , You can apply your validation rules to all the text box in the div by using following code:
$("#other_leaders :input[type='text']").each(function(){
if($(this).val()=="" || !RegExpression.test($(this).val()))
{
$(this).addClass('custom-error')
fnameflag = 0;
}
});
As you have just one element so there is no need to have a loop over it:
var $othLeader = $("input[name*='other_leader_fname']");
if($othLeader.val()=="" || !RegExpression.test($othLeader.val())){
$(this).addClass('custom-error');
fnameflag = 0;
}
And if you have form then you can validate this in your form's submit function.
You can iterate over the .controls using the each() and check for filled inputs in each group using filter for performing the validation as follows:
$('.controls').each(function(){
var $inputs = $(this).find('input');
var filled = $inputs.filter(function(){
return this.value != "";
});
if(filled.length){
$inputs.each(function(){
if($(this).val()=="" || !RegExpression.test($(this).val()))
{
$(this).addClass('custom-error')
fnameflag = 0;
}
})
}
});
Demo
side note: since the above is a template for dynamically generated content, You should remove the id and use class instead since id should be unique in a document.

How to autopopulate div class form

I have an Autoresponder email form on my page.
Below is part of the code of that form, for the email to be entered by customer:
<div id = "af-form-45" class = "af-form" >
<div id = "af-body-45" class = "af-body af-standards" >
<div class = "af-element" >
<label class = "previewLabel" for = "awf_field-57" > </label>
<div class="af-textWrap">
<input class="text" id="awf_field-57" type="text" name="email" value="Form 555" tabindex="500" onfocus=" if (this.value == 'Form 555') { this.value = ''; }" onblur="if (this.value == '') { this.value='Form 555';} " / >
</div>
<div class="af-clear"> </div>
</div>
</div>
</div>
</div>
The prepopulated email value in that form is 'Form 555'. Meaning that is someone lands on the page and sees the form, it already has the value of 'Form 555'
What I need to do is to pre-populate the field not with 'Form 555', but with certain value, which I get from running a Javascript in my header (that Javascript is long, so I'm not including it here).
But for example, if I put the below code on the page, it will return a certain email address (which I need to be prepopulated in the form).
<script type="text/javascript">formData.display("email")</script>
So I need to do the same, but inside that div class form - this email address to be added instead of 'Form 555' prepopulated value
Thanks!
You can set val throu
$("#awf_field").val(formData.display("email"));
Pretty simple, is there something else you are struggling with?
Change
<script type="text/javascript">formData.display("email")</script>
to
<script type="text/javascript">
$('#awf_field').val(formData.display("email"))
</script>

Categories