I'm new to HTML/CSS and having an issue I just cannot google my way out of.
I've setup a search box which takes you to google.
<form action="https://www.google.co.uk/search"target="_blank">
<input type="text" name="q">
<button>Search Google</button>
</form>
This works for Google, you can type in a search, click the button and it takes you to Google with the search.
But when I try the same for Instagram it takes me to an error page.
<form action="https://www.instagram.com/explore/tags/"target="_blank">
<input type="text" name="q">
<button>Search instagram</button>
</form>
Example if you type in #Coding it takes you to this page:
https://www.instagram.com/explore/tags/?q=%23coding
Instead of this page:
https://www.instagram.com/explore/tags/coding/
Not sure what I'm doing wrong, any suggestions to help me out please :)
Google accepts GET param q as a string to search. So Your first form works as needed.
While instagram adds search query to URL.
You need a little help of JS to make Your second form work as needed:
const form = document.querySelector("form");
const q = document.querySelector("input[name='q']");
form.addEventListener("submit", (event) => {
event.preventDefault();
const url = form.action + '/' + q.value;
window.open(url);
});
Working example: https://jsfiddle.net/asertfy5/1/
One approach is below, and seems to satisfy your request as written; explanations are in the code as comments:
// defining a named handler for the <form> element's submission, using
// Arrow syntax, and passing the Event Object automagically from the
// (later) use of EventTarget.addEventListener():
const formHandler = (event) => {
// here we make use of the event.preventDefault() method to
// prevent the <form> from being submitted:
event.preventDefault();
// we get a reference to the current <form> element to which the
// event-handler is bound:
let form = event.currentTarget,
// an Object of functions to handle the submission process,
// based on the supplied attribute-value of the custom
// data-* attribute in the <form> tag:
format = {
getParameters: function() {
// if GET parameters are used then we can simply
// submit the <form> programatically, taking
// advantage of the fact that programmatic events
// don't call event-handlers by default, so this
// submission bypasses the <form> element's
// submit-handler:
form.submit();
},
rest: function() {
// if we're accessing a REST API, then we simply
// use Window.open(), and pass the relevant URL string;
// this function has access to the variables declared
// outside of itself, so here we create the URL by
// retrieving the path from the <form> element's
// action attribute and concatenating that with the
// trimmed value of the <input> element with the
// name of 'q' (this is not designed to handle multiple
// words, however; if that's a requirement please leave
// a comment but that's not ordinarily how a REST API
// would work):
window.open(`${form.action}${form.q.value.trim()}`);
}
};
// here we call the function held within the format Object,
// after determining the appropriate search-method from the
// <form> element's custom data-searchmethod attribute and
// using bracket-notation, in place of dot-notation, to access
// that function:
format[form.dataset.searchmethod]();
};
// we iterate over all <form> elements using document.querySelectorAll() to
// retrieve those elements, and then chaining with NodeList.prototype.forEach()
// to handle iteration:
document.querySelectorAll('form').forEach(
// here we pass the current <form> element into the function,
// and bind the formHandler() function as the event-handler for
// the 'submit' event on that <form>:
(form) => form.addEventListener('submit', formHandler)
);
/* all CSS is irrelevant, it's just to make the demo
a little prettier */
*,
::before,
::after {
box-sizing: border-box;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
margin: 0;
padding: 0;
}
form {
display: flex;
gap: 0.25em;
justify-content: start;
margin-block: 1em;
margin-inline: auto;
width: 80vw;
}
button {
padding-inline: 0.3em;
}
<!-- within the <form> element's opening tag I added a custom data-*
attribute to provide information about how the search functionality
is intended to work, in the case of Google we're using, and therefore
passing, GET parameters: -->
<form action="https://www.google.co.uk/search"
target="_blank"
data-searchmethod="getParameters">
<input type="text" name="q">
<button>Search Google</button>
</form>
<!-- in the Instagram search, we're using a REST endpoint: -->
<form action="https://www.instagram.com/explore/tags/" target="_blank" data-searchmethod="rest">
<input type="text" name="q">
<button>Search instagram</button>
</form>
JS Fiddle demo.
Please note that, while the code above is identical between JS Fiddle and the Stack Snippet the Snippet won't work here on Stack Overflow due to the security restrictions of the sandbox, so please test in the linked JS Fiddle.
I feel that it's worth adding that during my testing the URL for Instagram was created as you desired, but the Instagram site blocked the page from loading. This may, or may not, be the result of something on my end but while I can verify the URL of the opened page matches your request, I can't verify that it loads the content you desire.
References:
Arrow functions.
Bracket notation.
document.querySelector().
document.querySelectorAll().
Dot notation.
Event.currentTarget.
Event.preventDefault().
EventTarget.addEventListener().
HTMLElement.dataset.
Window.open().
Related
I integrated the new hidden reCAPTCHA (v2) framework which by default verifies the user with the click event of the submit button. But this event is triggered before the built-in HTML form validation. I am looking for a way to make it in the expected order: form validation first, reCAPTCHA after.
You have to do it programmatically thanks to a new v2 grecaptcha method: grecaptcha.execute() so that recaptcha doesn't replace the button's default click event which was preventing the default HTML5 form validation.
The event path is:
Submit button click event: browser built-in form validation
Form submit event: call grecaptcha.execute()
reCAPTCHA callback: submit the form
$('#form-contact').submit(function (event) {
event.preventDefault();
grecaptcha.reset();
grecaptcha.execute();
});
function formSubmit(response) {
// submit the form which now includes a g-recaptcha-response input
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.google.com/recaptcha/api.js"></script>
<form action="?">
<div class="g-recaptcha"
data-sitekey="your-key"
data-size="invisible"
data-callback="formSubmit">
</div>
<button type="submit">Submit</button>
</form>
Here is my solution to get HTML5 validation + Invisible recaptcha:
HTML:
<form id="my-form">
<!-- Your form fields ... -->
<div class="g-recaptcha"
data-sitekey="..."
data-callback="submitMyForm"
data-size="invisible">
</div>
<button type="submit">Submit</button>
</form>
JS:
var myForm = $('my-form');
function submitMyForm () {
myForm.trigger('submit', [true]);
}
$(function () {
myForm.on('submit', function (e, skipRecaptcha) {
if(skipRecaptcha) {
return;
}
e.preventDefault();
grecaptcha.execute();
});
})
Hi got a working solution here. Working with invisible Recaptcha.
jQuery(document).ready(function() {
var commentform = jQuery("#commentform");
commentform.on("click", "#submit-comment", function(e) {
if(commentform[0].checkValidity()) {
e.preventDefault();
grecaptcha.execute();
}
});
});
function submitCommentForm(data) {
document.getElementById("commentform").submit();
}
<form action="blaba.php" method="post" id="commentform" class="comment-form">
<div class="form-submit">
<div data-callback="submitCommentForm" data-sitekey="yourkey" class="g-recaptcha" data-size="invisible">
<button id="submit-comment">Leave a comment</button>
</div>
</form>
I had this problem as the default method seems to override the html5 form validation. I also wanted all code to be generic rather than hard coding any functions/element names. In the end I came up with the following code using the v3 api -
HTML
<form method="post" action="?" class="ui-recaptcha" name="my_form_name">
...
<input type="submit" value="Submit">
</form>
<script src="//www.google.com/recaptcha/api.js?render={key}" async defer></script>
Javascript (I'm using jQuery but would be fairly easy to adapt to vanilla js)
$('.ui-recaptcha').submit(e => {
var form = e.target;
if( $(form).data('recaptcha-done') )
return;
e.preventDefault();
grecaptcha.execute('{key}', {'action': $(form).attr('name')}).then(token => {
$(form).append($('<input>').attr({'type': 'hidden', 'name': 'g-recaptcha-response', 'value': token}));
$(form).data('recaptcha-done', true);
$(form).submit();
});
});
I found that just calling submit as in some examples above caused a loop for me, which would make sense seeing as the recaptcha handler runs on the submit event.
This runs recaptcha for any ui-recaptcha form, passes the form name attribute as the action which can be seen in reCaptcha console, and then inserts the token into the form. Once run it sets a data attribute on the form so the recursive call to submit doesn't try to run recaptcha again.
Here's my solution.
Uses reCaptcha v3 (invisible) docs
Uses native HTML5 form validation
Uses pure JS
Uses standard POST processing (can be modified to AJAX)
Add as many forms as needed, just change the 'UNIQUE_FORM_ID' in the two places, and update the POST_URL for the form.
Ensure you use your own key in the locations of 'RECAPTCHA_SITE_KEY'.
<form id="UNIQUE_FORM_ID" method="post" action="POST_URL">
<!-- ** Notice ** this hidden input field that will later send our g-recaptcha token back to our server -->
<input type="hidden" name="g-recaptcha-response" value="">
<!-- Add other hidden nonce fields -->
<!-- Required field -->
<input name="fullname" type="text" placeholder="Full Name" required>
<!-- Submit button -->
<!-- ** Notice ** the 'form' attribute; using SAME value as it's parent's form id, above. -->
<!-- ** Notice ** the 'onclick' attribute; be sure to pass event -->
<button type="submit" form="UNIQUE_FORM_ID" onclick="formSubmitBtn(event)">Send</button>
</form>
<!-- Only add scripts once -->
<!-- ** Notice ** to manually call grecaptcha, our site key must be included when loading api.js using the 'render' query param -->
<script src="https://www.google.com/recaptcha/api.js?render=RECAPTCHA_SITE_KEY"></script>
<script>
/**
* Handles form submissions for Google recaptcha v3.
* Allows for HTML5 form validation to complete before processing.
*/
function formSubmitBtn($event) {
/**
* Checks the validity of the form.
* Return if invalid; HTML5 validation errors should display.
*/
if (!$event.target.form.checkValidity()) {
return;
}
/**
* Form is client-side valid; taking over the remainder of processing.
*/
$event.preventDefault();
grecaptcha.ready(function() {
grecaptcha.execute("RECAPTCHA_SITE_KEY", { action: 'submit' }).then(function(token) {
/**
* Adds the token g-recaptcha-response token to our hidden form element.
* ** Notice ** we our referencing the specific form's input element by name here (do not use IDs).
*/
$event.target.form.elements['g-recaptcha-response'].value = token;
/**
* Use the form API directly to submit the form.
*/
$event.target.form.submit();
});
});
}
</script>
let siteKey = "...";
$("form").submit(function (eventObj) {
var myForm = this;
eventObj.preventDefault();
grecaptcha.execute( siteKey, {
action: "submit"
})
.then(function (token) {
$('<input />').attr('type', 'hidden')
.attr('name', "g_recaptcha_response")
.attr('value', token)
.appendTo(myForm);
myForm.submit();
});
});
This will execute recapcha, wait for response, add hidden attribute g_recaptcha_response to any form when browser try to submit it and then actually submit it. You need global variable siteKey
I was wanting the same behavior, but using the new recaptcha, the invisible one. After looking at some code and testing some stuff, I got into this. The main difference is that this uses the default browser validation as well:
var contact_form;
$(function() {
contact_form = $('#contact-form');
contact_form.submit(function (event) {
if ( ! contact_form.data('passed')) {
event.preventDefault();
grecaptcha.execute();
}
});
});
function sendContactForm(token) {
contact_form.data('passed', true);
contact_form.submit();
}
It basically stores the jquery form object in a global var, including, it uses sendContactForm as the callback, but when called by the recaptcha, it sets a data var named passed, which allows the form to not be prevented. It's exactly the same behavior as recaptcha would normally do, but with that condition.
Update: re-looking at my code right reminds me that it probably needs a way to restore data passed to false after grecaptcha's execution. Consider that if you'll implement this.
This solution is similar to solution by #PigBoT but with the addition of reportValidity() and is using ReCAPTCHA v3
Credit to https://github.com/ambethia/recaptcha/issues/302#issuecomment-621794131
<script src="https://www.google.com/recaptcha/api.js"></script>
<script type="text/javascript">
function contactOnSubmit(token) {
var contactForm = document.getElementById('contactUs');
if(contactForm.checkValidity()) {
//SERVER SIDE VALIDATION here,
//on success, contactForm.submit();
} else {
grecaptcha.reset();
contactForm.reportValidity();
}
}
</script>
Form (id="contactUs")
<button class="g-recaptcha" data-sitekey="..." data-callback="contactOnSubmit" data-action="submit">Submit</button>
"Can I Use" site currently reports 97% uses have support for checkValidity() https://caniuse.com/?search=checkValidity
I have an Angular form. The fields are validated using the ng-pattern attribute. I also have a reset button. I'm using the Ui.Utils Event Binder to handle the reset event like so:
<form name="searchForm" id="searchForm" ui-event="{reset: 'reset(searchForm)'}" ng-submit="search()">
<div>
<label>
Area Code
<input type="tel" name="areaCode" ng-model="areaCode" ng-pattern="/^([0-9]{3})?$/">
</label>
<div ng-messages="searchForm.areaCode.$error">
<div class="error" ng-message="pattern">The area code must be three digits</div>
</div>
</div>
<div>
<label>
Phone Number
<input type="tel" name="phoneNumber" ng-model="phoneNumber" ng-pattern="/^([0-9]{7})?$/">
</label>
<div ng-messages="searchForm.phoneNumber.$error">
<div class="error" ng-message="pattern">The phone number must be seven digits</div>
</div>
</div>
<br>
<div>
<button type="reset">Reset</button>
<button type="submit" ng-disabled="searchForm.$invalid">Search</button>
</div>
</form>
As you can see, when the form is reset it calls the reset method on the $scope. Here's what the entire controller looks like:
angular.module('app').controller('mainController', function($scope) {
$scope.resetCount = 0;
$scope.reset = function(form) {
form.$setPristine();
form.$setUntouched();
$scope.resetCount++;
};
$scope.search = function() {
alert('Searching');
};
});
I'm calling form.$setPristine() and form.$setUntouched, following the advice from another question here on Stack Overflow. The only reason I added the counter was to prove that the code is being called (which it is).
The problem is that even after reseting the form, the validation messages don't go away. You can see the full code on Plunker. Here's a screenshot showing that the errors don't go away:
I started with the comment from #Brett and built upon it. I actually have multiple forms and each form has many fields (more than just the two shown). So I wanted a general solution.
I noticed that the Angular form object has a property for each control (input, select, textarea, etc) as well as some other Angular properties. Each of the Angular properties, though, begins with a dollar sign ($). So I ended up doing this (including the comment for the benefit of other programmers):
$scope.reset = function(form) {
// Each control (input, select, textarea, etc) gets added as a property of the form.
// The form has other built-in properties as well. However it's easy to filter those out,
// because the Angular team has chosen to prefix each one with a dollar sign.
// So, we just avoid those properties that begin with a dollar sign.
let controlNames = Object.keys(form).filter(key => key.indexOf('$') !== 0);
// Set each control back to undefined. This is the only way to clear validation messages.
// Calling `form.$setPristine()` won't do it (even though you wish it would).
for (let name of controlNames) {
let control = form[name];
control.$setViewValue(undefined);
}
form.$setPristine();
form.$setUntouched();
};
$scope.search = {areaCode: xxxx, phoneNumber: yyyy}
Structure all models in your form in one place like above, so you can clear it like this:
$scope.search = angular.copy({});
After that you can just call this for reset the validation:
$scope.search_form.$setPristine();
$scope.search_form.$setUntouched();
$scope.search_form.$rollbackViewValue();
There doesn't seem to be an easy way to reset the $errors in angular. The best way would probably be to reload the current page to start with a new form. Alternatively you have to remove all $error manually with this script:
form.$setPristine(true);
form.$setUntouched(true);
// iterate over all from properties
angular.forEach(form, function(ctrl, name) {
// ignore angular fields and functions
if (name.indexOf('$') != 0) {
// iterate over all $errors for each field
angular.forEach(ctrl.$error, function(value, name) {
// reset validity
ctrl.$setValidity(name, null);
});
}
});
$scope.resetCount++;
You can add a validation flag and show or hide errors according to its value with ng-if or ng-show in your HTML. The form has a $valid flag you can send to your controller.
ng-if will remove or recreate the element to the DOM, while ng-show will add it but won't show it (depending on the flag value).
EDIT: As pointed by Michael, if form is disabled, the way I pointed won't work because the form is never submitted. Updated the code accordingly.
HTML
<form name="searchForm" id="searchForm" ui-event="{reset: 'reset(searchForm)'}" ng-submit="search()">
<div>
<label>
Area Code
<input type="tel" name="areaCode" ng-model="areaCode" ng-pattern="/^([0-9]{3})?$/">
</label>
<div ng-messages="searchForm.areaCode.$error">
<div class="error" ng-message="pattern" ng-if="searchForm.areaCode.$dirty">The area code must be three digits</div>
</div>
</div>
<div>
<label>
Phone Number
<input type="tel" name="phoneNumber" ng-model="phoneNumber" ng-pattern="/^([0-9]{7})?$/">
</label>
<div ng-messages="searchForm.phoneNumber.$error">
<div class="error" ng-message="pattern" ng-if="searchForm.phoneNumber.$dirty">The phone number must be seven digits</div>
</div>
</div>
<br>
<div>
<button type="reset">Reset</button>
<button type="submit" ng-disabled="searchForm.$invalid">Search</button>
</div>
</form>
JS
$scope.search = function() {
alert('Searching');
};
$scope.reset = function(form) {
form.$setPristine();
form.$setUntouched();
$scope.resetCount++;
};
Codepen with working solution: http://codepen.io/anon/pen/zGPZoB
It looks like I got to do the right behavior at reset. Unfortunately, using the standard reset failed. I also do not include the library ui-event. So my code is a little different from yours, but it does what you need.
<form name="searchForm" id="searchForm" ng-submit="search()">
pristine = {{searchForm.$pristine}} valid ={{searchForm.$valid}}
<div>
<label>
Area Code
<input type="tel" required name="areaCode" ng-model="obj.areaCode" ng-pattern="/^([0-9]{3})?$/" ng-model-options="{ allowInvalid: true }">
</label>
<div ng-messages="searchForm.areaCode.$error">
<div class="error" ng-message="pattern">The area code must be three digits</div>
<div class="error" ng-message="required">The area code is required</div>
</div>
</div>
<div>
<label>
Phone Number
<input type="tel" required name="phoneNumber" ng-model="obj.phoneNumber" ng-pattern="/^([0-9]{7})?$/" ng-model-options="{ allowInvalid: true }">
</label>
<div ng-messages="searchForm.phoneNumber.$error">
<div class="error" ng-message="pattern">The phone number must be seven digits</div>
<div class="error" ng-message="required">The phone number is required</div>
</div>
</div>
<br>
<div>
<button ng-click="reset(searchForm)" type="reset">Reset</button>
<button type="submit" ng-disabled="searchForm.$invalid">Search</button>
</div>
</form>
And JS:
$scope.resetCount = 0;
$scope.obj = {};
$scope.reset = function(form_) {
$scope.resetCount++;
$scope.obj = {};
form_.$setPristine();
form_.$setUntouched();
console.log($scope.resetCount);
};
$scope.search = function() {
alert('Searching');
};
Live example on jsfiddle.
Note the directive ng-model-options="{allowinvalid: true}". Use it necessarily, or until the entry field will not be valid, the model value is not recorded. Therefore, the reset will not operate.
P.S. Put value (areaCode, phoneNumber) on the object simplifies purification.
Following worked for me
let form = this.$scope.myForm;
let controlNames = Object.keys(form).filter(key => key.indexOf('$') !== 0);
for (let name of controlNames) {
let control = form [name];
control.$error = {};
}
In Short: to get rid of ng-messages errors you need to clear out the $error object for each form item.
further to #battmanz 's answer, but without using any ES6 syntax to support older browsers.
$scope.resetForm = function (form) {
try {
var controlNames = Object.keys(form).filter(function (key) { return key.indexOf('$') !== 0 });
console.log(controlNames);
for (var x = 0; x < controlNames.length; x++) {
form[controlNames[x]].$setViewValue(undefined);
}
form.$setPristine();
form.$setUntouched();
} catch (e) {
console.log('Error in Reset');
console.log(e);
}
};
I had the same problem and tried to do battmanz solution (accepted answer).
I'm pretty sure his answer is really good, but however for me it wasn't working.
I am using ng-model to bind data, and angular material library for the inputs and ng-message directives for error message , so maybe what I will say will be useful only for people using the same configuration.
I took a lot of look at the formController object in javascript, in fact there is a lot of $ angular function as battmanz noted, and there is in addition, your fields names, which are object with some functions in its fields.
So what is clearing your form ?
Usually I see a form as a json object, and all the fields are binded to a key of this json object.
//lets call here this json vm.form
vm.form = {};
//you should have something as ng-model = "vm.form.name" in your view
So at first to clear the form I just did callback of submiting form :
vm.form = {};
And as explained in this question, ng-messages won't disappear with that, that's really bad.
When I used battmanz solution as he wrote it, the messages didn't appear anymore, but the fields were not empty anymore after submiting, even if I wrote
vm.form = {};
And I found out it was normal, because using his solution actually remove the model binding from the form, because it sets all the fields to undefined.
So the text was still in the view because somehow there wan't any binding anymore and it decided to stay in the HTML.
So what did I do ?
Actually I just clear the field (setting the binding to {}), and used just
form.$setPristine();
form.$setUntouched();
Actually it seems logical, since the binding is still here, the values in the form are now empty, and angular ng-messages directive is triggering only if the form is not untouched, so I think it's normal after all.
Final (very simple) code is that :
function reset(form) {
form.$setPristine();
form.$setUntouched();
};
A big problem I encountered with that :
Only once, the callback seems to have fucked up somewhere, and somehow the fields weren't empty (it was like I didn't click on the submit button).
When I clicked again, the date sent was empty. That even more weird because my submit button is supposed to be disabled when a required field is not filled with the good pattern, and empty is certainly not a good one.
I don't know if my way of doing is the best or even correct, if you have any critic/suggestion or any though about the problem I encountered, please let me know, I always love to step up in angularJS.
Hope this will help someone and sorry for the bad english.
You can pass your loginForm object into the function ng-click="userCtrl.login(loginForm)
and in the function call
this.login = function (loginForm){
loginForm.$setPristine();
loginForm.$setUntouched();
}
So none of the answers were completely working for me. Esp, clearing the view value, so I combined all the answers clearing view value, clearing errors and clearing the selection with j query(provided the fields are input and name same as model name)
var modelNames = Object.keys($scope.form).filter(key => key.indexOf('$') !== 0);
modelNames.forEach(function(name){
var model = $scope.form[name];
model.$setViewValue(undefined);
jq('input[name='+name+']').val('');
angular.forEach(model.$error, function(value, name) {
// reset validity
model.$setValidity(name, null);
});
});
$scope.form.$setPristine();
$scope.form.$setUntouched();
I'm trying to get the value of a mobile number textbox to validate its input value using angular.js. I'm a newbie in using angular.js and not so sure how to implement those events and put some javascript to validate or manipulate the form inputs on my html code.
This is my HTML:
<div>
<label for="mobile_number">Mobile Number</label>
<input type="text" id="mobile_number" placeholder="+639178983214" required
ngcontroller="RegisterDataController" ng-keydown="keydown">
</div>
And my controller:
function RegisterDataController($scope, $element) {
console.log('register data controller');
console.log($element);
$scope.keydown = function(keyEvent) {
console.log('keydown -'+keyEvent);
};
}
I'm not sure how to use the keydown event in angular.js, I also searched how to properly use it. And can i validate my inputs on the directives? Or should I use a controller like what I've done to use the events like keydown or keypress?
Update:
ngKeypress, ngKeydown and ngKeyup are now part of AngularJS.
<!-- you can, for example, specify an expression to evaluate -->
<input ng-keypress="count = count + 1" ng-init="count=0">
<!-- or call a controller/directive method and pass $event as parameter.
With access to $event you can now do stuff like
finding which key was pressed -->
<input ng-keypress="changed($event)">
Read more here:
https://docs.angularjs.org/api/ng/directive/ngKeypress
https://docs.angularjs.org/api/ng/directive/ngKeydown
https://docs.angularjs.org/api/ng/directive/ngKeyup
Earlier solutions:
Solution 1: Use ng-change with ng-model
<input type="text" placeholder="+639178983214" ng-model="mobileNumber"
ng-controller="RegisterDataController" ng-change="keydown()">
JS:
function RegisterDataController($scope) {
$scope.keydown = function() {
/* validate $scope.mobileNumber here*/
};
}
Solution 2. Use $watch
<input type="text" placeholder="+639178983214" ng-model="mobileNumber"
ng-controller="RegisterDataController">
JS:
$scope.$watch("mobileNumber", function(newValue, oldValue) {
/* change noticed */
});
You were on the right track with your "ng-keydown" attribute on the input, but you missed a simple step. Just because you put the ng-keydown attribute there, doesn't mean angular knows what to do with it. That's where "directives" come into play. You used the attribute correctly, but you now need to write a directive that will tell angular what to do when it sees that attribute on an html element.
The following is an example of how you would do that. We'll rename the directive from ng-keydown to on-keydown (to avoid breaking the "best practice" found here):
var mod = angular.module('mydirectives');
mod.directive('onKeydown', function() {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
// this next line will convert the string
// function name into an actual function
var functionToCall = scope.$eval(attrs.ngKeydown);
elem.on('keydown', function(e){
// on the keydown event, call my function
// and pass it the keycode of the key
// that was pressed
// ex: if ENTER was pressed, e.which == 13
functionToCall(e.which);
});
}
};
});
The directive simple tells angular that when it sees an HTML attribute called "ng-keydown", it should listen to the element that has that attribute and call whatever function is passed to it. In the html you would have the following:
<input type="text" on-keydown="onKeydown">
And then in your controller (just like you already had), you would add a function to your controller's scope that is called "onKeydown", like so:
$scope.onKeydown = function(keycode){
// do something with the keycode
}
Hopefully that helps either you or someone else who wants to know
You can checkout Angular UI # http://angular-ui.github.io/ui-utils/ which provide details event handle callback function for detecting keydown,keyup,keypress
(also Enter key, backspace key, alter key ,control key)
<textarea ui-keydown="{27:'keydownCallback($event)'}"></textarea>
<textarea ui-keypress="{13:'keypressCallback($event)'}"></textarea>
<textarea ui-keydown="{'enter alt-space':'keypressCallback($event)'}"> </textarea>
<textarea ui-keyup="{'enter':'keypressCallback($event)'}"> </textarea>
JavaScript code using ng-controller:
$scope.checkkey = function (event) {
alert(event.keyCode); //this will show the ASCII value of the key pressed
}
In HTML:
<input type="text" ng-keypress="checkkey($event)" />
You can now place your checks and other conditions using the keyCode method.
I have a form with several different fieldsets. I have some jQuery that displays the field sets to the users one at a time. For browsers that support HTML5 validation, I'd love to make use of it. However, I need to do it on my terms. I'm using JQuery.
When a user clicks a JS Link to move to the next fieldset, I need the validation to happen on the current fieldset and block the user from moving forward if there is issues.
Ideally, as the user loses focus on an element, validation will occur.
Currently have novalidate going and using jQuery. Would prefer to use the native method. :)
TL;DR: Not caring about old browsers? Use form.reportValidity().
Need legacy browser support? Read on.
It actually is possible to trigger validation manually.
I'll use plain JavaScript in my answer to improve reusability, no jQuery is needed.
Assume the following HTML form:
<form>
<input required>
<button type="button">Trigger validation</button>
</form>
And let's grab our UI elements in JavaScript:
var form = document.querySelector('form')
var triggerButton = document.querySelector('button')
Don't need support for legacy browsers like Internet Explorer? This is for you.
All modern browsers support the reportValidity() method on form elements.
triggerButton.onclick = function () {
form.reportValidity()
}
That's it, we're done. Also, here's a simple CodePen using this approach.
Approach for older browsers
Below is a detailed explanation how reportValidity() can be emulated in older browsers.
However, you don't need to copy&paste those code blocks into your project yourself — there is a ponyfill/polyfill readily available for you.
Where reportValidity() is not supported, we need to trick the browser a little bit. So, what will we do?
Check validity of the form by calling form.checkValidity(). This will tell us if the form is valid, but not show the validation UI.
If the form is invalid, we create a temporary submit button and trigger a click on it. Since the form is not valid, we know it won't actually submit, however, it will show validation hints to the user. We'll remove the temporary submit button immedtiately, so it will never be visible to the user.
If the form is valid, we don't need to interfere at all and let the user proceed.
In code:
triggerButton.onclick = function () {
// Form is invalid!
if (!form.checkValidity()) {
// Create the temporary button, click and remove it
var tmpSubmit = document.createElement('button')
form.appendChild(tmpSubmit)
tmpSubmit.click()
form.removeChild(tmpSubmit)
} else {
// Form is valid, let the user proceed or do whatever we need to
}
}
This code will work in pretty much any common browser (I've tested it successfully down to IE11).
Here's a working CodePen example.
You can't trigger the native validation UI (see edit below), but you can easily take advantage of the validation API on arbitrary input elements:
$('input').blur(function(event) {
event.target.checkValidity();
}).bind('invalid', function(event) {
setTimeout(function() { $(event.target).focus();}, 50);
});
The first event fires checkValidity on every input element as soon as it loses focus, if the element is invalid then the corresponding event will be fired and trapped by the second event handler. This one sets the focus back to the element, but that could be quite annoying, I assume you have a better solution for notifying about the errors. Here's a working example of my code above.
EDIT: All modern browsers support the reportValidity() method for native HTML5 validation, per this answer.
In some extent, You CAN trigger HTML5 form validation and show hints to user without submitting the form!
Two button, one for validate, one for submit
Set a onclick listener on the validate button to set a global flag(say justValidate) to indicate this click is intended to check the validation of the form.
And set a onclick listener on the submit button to set the justValidate flag to false.
Then in the onsubmit handler of the form, you check the flag justValidate to decide the returning value and invoke the preventDefault() to stop the form to submit. As you know, the HTML5 form validation(and the GUI hint to user) is preformed before the onsubmit event, and even if the form is VALID you can stop the form submit by returning false or invoke preventDefault().
And, in HTML5 you have a method to check the form's validation: the form.checkValidity(), then in you can know if the form is validate or not in your code.
OK, here is the demo:
http://jsbin.com/buvuku/2/edit
var field = $("#field")
field.keyup(function(ev){
if(field[0].value.length < 10) {
field[0].setCustomValidity("characters less than 10")
}else if (field[0].value.length === 10) {
field[0].setCustomValidity("characters equal to 10")
}else if (field[0].value.length > 10 && field[0].value.length < 20) {
field[0].setCustomValidity("characters greater than 10 and less than 20")
}else if(field[0].validity.typeMismatch) {
field[0].setCustomValidity("wrong email message")
}else {
field[0].setCustomValidity("") // no more errors
}
field[0].reportValidity()
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="email" id="field">
Somewhat easy to make add or remove HTML5 validation to fieldsets.
$('form').each(function(){
// CLEAR OUT ALL THE HTML5 REQUIRED ATTRS
$(this).find('.required').attr('required', false);
// ADD THEM BACK TO THE CURRENT FIELDSET
// I'M JUST USING A CLASS TO IDENTIFY REQUIRED FIELDS
$(this).find('fieldset.current .required').attr('required', true);
$(this).submit(function(){
var current = $(this).find('fieldset.current')
var next = $(current).next()
// MOVE THE CURRENT MARKER
$(current).removeClass('current');
$(next).addClass('current');
// ADD THE REQUIRED TAGS TO THE NEXT PART
// NO NEED TO REMOVE THE OLD ONES
// SINCE THEY SHOULD BE FILLED OUT CORRECTLY
$(next).find('.required').attr('required', true);
});
});
I seem to find the trick:
Just remove the form target attribute, then use a submit button to validate the form and show hints, check if form valid via JavaScript, and then post whatever. The following code works for me:
<form>
<input name="foo" required>
<button id="submit">Submit</button>
</form>
<script>
$('#submit').click( function(e){
var isValid = true;
$('form input').map(function() {
isValid &= this.validity['valid'] ;
}) ;
if (isValid) {
console.log('valid!');
// post something..
} else
console.log('not valid!');
});
</script>
Html Code:
<form class="validateDontSubmit">
....
<button style="dislay:none">submit</button>
</form>
<button class="outside"></button>
javascript( using Jquery):
<script type="text/javascript">
$(document).on('submit','.validateDontSubmit',function (e) {
//prevent the form from doing a submit
e.preventDefault();
return false;
})
$(document).ready(function(){
// using button outside trigger click
$('.outside').click(function() {
$('.validateDontSubmit button').trigger('click');
});
});
</script>
Hope this will help you
For input field
<input id="PrimaryPhNumber" type="text" name="mobile" required
pattern="^[789]\d{9}$" minlenght="10" maxLength="10" placeholder="Eg: 9444400000"
class="inputBoxCss"/>
$('#PrimaryPhNumber').keyup(function (e) {
console.log(e)
let field=$(this)
if(Number(field.val()).toString()=="NaN"){
field.val('');
field.focus();
field[0].setCustomValidity('Please enter a valid phone number');
field[0].reportValidity()
$(":focus").css("border", "2px solid red");
}
})
$('#id').get(0).reportValidity();
This will trigger the input with ID specified. Use ".classname" for classes.
When there is a very complex (especially asynchronous) validation process, there is a simple workaround:
<form id="form1">
<input type="button" onclick="javascript:submitIfVeryComplexValidationIsOk()" />
<input type="submit" id="form1_submit_hidden" style="display:none" />
</form>
...
<script>
function submitIfVeryComplexValidationIsOk() {
var form1 = document.forms['form1']
if (!form1.checkValidity()) {
$("#form1_submit_hidden").click()
return
}
if (checkForVeryComplexValidation() === 'Ok') {
form1.submit()
} else {
alert('form is invalid')
}
}
</script>
Another way to resolve this problem:
$('input').oninvalid(function (event, errorMessage) {
event.target.focus();
});
I am trying to use the form method checkValidity().
http://html5test.com/ tells me that my browser (Chrome) support the form-level checkValidity method.
However, using jsfiddle http://jsfiddle.net/LcgnQ/2/ I have tried the following html and javascript snippets:
<form id="profileform" name="profileform">
<input type="text" id="firstname" required>
<input type="button" id="testbutton" value="Test">
</form>
$('#testbutton').bind('click',function(){
try{
alert($('#profileform').checkValidity());
}
catch(err){alert('err='+err)};
});
I'm getting an error: object has no method checkValidity()
What am I doing wrong?
Try:
$('#profileform')[0].checkValidity()
When you select $('#profileform') you get a jQuery object array. To access actual DOM properties you must select the first item in the array, which is the raw DOM element.
#robertc 's Answer is perfect. Anyway I'd just add another way to do it using jQuery's .get([index]) function. It also retrieves the DOM element for the given index, or all of the matched DOM elements if there's no index declared.
In the end it is exactly the same, only written in a bit more verbose way:
$('#profileform').get(0).checkValidity()
Leaving you the docs right here: https://api.jquery.com/get/
Just another way:
// Fetch all the forms we want to apply custom Bootstrap validation styles to
var forms = document.getElementsByName('profileform');
// Loop over them and prevent submission
var validation = Array.prototype.filter.call(forms, function (form) {
form.addEventListener('submit', function (event) {
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
}, false);
});