Serverside validation of AngularJS forms, use $setValidity in the view controller? - javascript

Or put in tl;dr code:
form.email.$setValidity('conflict', false);
is too sticky for my simple serverside validation flow.
I'm trying to get the form to show good feedback in the event that the user enters an email address already in use by another customer. I'm running AngularJS v1.2 and have this template:
<form name="form">
<input name="email" type="email" ng-model="..." required>
</form>
<div ng-messages="form.email.$error">
<div ng-message="conflict">Email address already in use.</div>
</div>
In my controller, I'll handle the submit event and trigger the validation in my $http.post().error handler like this:
$http.post('api/form/submit/path/here').error(function(resp) {
if (resp.details === 'conflict')
$scope.form.email.$setValidity('conflict', false);
});
The problem is that when the user goes back and changes the value in the input field, the error message doesn't go away. It sticks around until I manually call $scope.form.setValidity();.
The docs say implement a custom directive with an ng-model dependency, but that seems super overkill for my purposes. I've also tried setting $scope.form.email.$valid = false; and $scope.form.email.$invalid = true; but those don't change the appearance of the textbox.

Nothing in your code modifies the conflict validation key, except for when $setValidity('conflict', false) is explicitly called. Since that is the only code setting the state of the conflict validation key and there is nothing else resetting it to true, it's expected behaviour that editing the textbox wouldn't reset its conflict validation state.
To get the behaviour that you want, you need to code for it. One way is to use ng-change.
<input name="email" type="email" ng-model="..." required ng-change="resetConflictState()">
$scope.resetConflictState = function() {
$scope.form.email.$setValidity('conflict', true);
}

Related

Google Chrome issue: Model for input type 'password' not updated but 'text' does when browser autofills saved values

I have a login Form that is validated using Angular validation as follows:
HTML for Input fields:
<input id="loginName" name="loginName" type="text" class="form-control" placeholder="Username" data-ng-model="loginName" data-ng-maxlength="246" required>
<input id="password" name="password" type="password" class="form-control" placeholder="Password" data-ng-model="password" data-ng-maxlength="246" required>
'Login' button is validated against loginName and password fields using angular validation.
In Google Chrome (other browsers behave as intended) when these fields are saved (with do you want to save username and password feature) when the page is refreshed, the model for input type 'text' -> $scope.loginName gets updated with the saved value on the other hand the model for input type 'password' -> $scope.password is always empty) and according to the validation logic form is declared invalid and the 'Login' button stays disabled even though both the input fields are populated with saved information (See first attached image).
The moment a keypress or mouse click even occurs(not necessarily on the input fields but anywhere on the web page), somehow the model for password is updated and form is validated as shown in the second image attached.
I tried using autofocus, custom autofocus directives, timeouts but it doesn't seem to work as intended.
any suggestions, probably moving the cursor to the end of the text field so that the form knows that the text has been entered in the password field by the browser?
Came across this: AngularJS browser autofill workaround by using a directive
NOTE: All he answers in above solution talk about input elements value, got by either .val() or .value methods but the tricky part is both return undefined in case of password input field.
But no luck!
Thanks.
You may use
$scope.$watch('modelValue',function(newVal,oldVal){
//your code action
});
which will keep on tracking the model value. Whenever the model has value, your code inside $scope.$watch will be triggered.
As suggested by Ziv Weissman (in the comments section above) and after wasting quite a few hours on this, I have abandoned the AngularJs style validation process for the Login button and the input type password as well.
Chrome pretends it does not have a value for the field as a security measure. There's no way to hack around that. from: here
I found a potential solution to this behaviour which involves re-capturing the password value after a delay.
Just add this directive,and call the directive from the input's
Directive code
Modeule.directive('autoComplete', function ($timeout) {
return function (scope, iElement, iAttrs) {
iElement.autocomplete({
source: scope[iAttrs.uiItems],
select: function () {
$timeout(function () {
iElement.trigger('input');
}, 0);
}
});
};
});
HTML code
<input type="text" ng-model="username" auto-complete />

How can I tell if an HTTP form has been submitted?

Let's use a simple little petition form designed to innocently gather billions of passwords:
<form action="/Petition/Sign" method="post">
<input class="form-control password" id="Password" name="Password" type="password">
<button type="submit" class="btn loginBtn">Sign</button>
</form>
I would like to be signaled in JavaScript code (as Angular-esque as possible) within this view when a successful post has taken place for whatever reason, the most obvious the being the 'Sign' button being clicked, but that even is only the beginning of the event I want to catch the end of.
The most simple I can think of is to create and clear a hidden input on the button's ngClick event, then poll for a fixed timeout until the value of that input is a constant one always expected back from POST requests.
NOTE: This is a tiny example of a great horde of such forms and converting then all to use Ajax calls with signaled completions is only on the very remote side of feasible and further from desirable. Unlike in other such questions I f have asked, I have no interest directly measuring the success of the call.
In pure javascript way-
var ele = [Your Form Element];
if(ele.addEventListener){
ele.addEventListener("submit", callback, false); //Modern browsers
}else if(ele.attachEvent){
ele.attachEvent('onsubmit', callback); //Old IE
}
In jquery -
$(element).submit(function(){
console.log('submitted);
});
Whereas in AngularJs-
There is ng-submit directive for this purpose.https://docs.angularjs.org/api/ng/directive/ngSubmit
<form ng-submit="callback()">
in controller
$scope.callback = function(){ /*...*/ };
Have a read here also - How can I listen to the form submit event in javascript?

Angular, required check on controller throughout multiple forms

I am trying to find a simple solution to a required input type of scenario. I have multiple small forms that all send on one button save on the bottom of the page. What I am trying to accomplish is something like ngRequired, however across the whole controller, not just the individual forms. So the desired effect is pretty simple - if any of the inputs aren't filled out - set a boolean( or something) to false that disables the save button at the bottom.
So my first attempt is like this -
I have a model on each of the required items - there are 10 items
then I have a function that checks when you try to click the button how many are chcked like this
if($scope.modeltracker1){
//if there is anything inside model 1 add 1 to the tracker
$scope.modeltracker += 1;
}
and if the counter is not 10, don't do anything (all required are not filled out)
if($scope.modeltracker != 10){
//do nothing because all required are not filed out
}else{
//run save, all required all filed out
}
So - I feel like there should be a much easier solution than my first attempt here. Maybe something along the lines of checking if any individual one of these required fields is false, don't fire? I know that ngRequied would be great for this, but unfortunately the way this has to be structured, it cannot be one large form. There has to be a much easier way to accomplish this task with angular.
Any input would be much appreciated, thanks for reading!!
You can use ng-form to nest your multiple forms. It allows using nested forms and validating multiple forms as one form.
So, you need to nest your multiple forms in one root form.
<div ng-controller="demoController">
<form name="parentForm">
<ng-form name="firstForm">
<input type="text" ng-model="firstModel" required>
</ng-form>
<ng-form name="secondForm">
<input type="text" ng-model="secondModel" required>
</ng-form>
</form>
</div>
Then, all you need to do is to check parent form's validation status.
angular.module('formDemo', [])
.controller('demoController', ['$scope', function($scope) {
if($scope.parentForm.$valid) {
//run save, all required all filed out
} else {
//do nothing because all required are not filed out
}
}]);
you can use myForm.$invalid directive, as explained here: Disable submit button when form invalid with AngularJS

The trouble with $scope

I have an input file element within an angular view/form. I'm using ng-upload like this:
<input id="img" type="file" name="image" onchange="angular.element(this).scope().setFile(this)">
<input id="imgname" type="hidden" value=""></div>
Since I can't tell angular to listen for changes on input[type="file"] element, I've created the method that updates the hidden input that just holds the current filename. That way I can run my validator on the second field.
Another field I have has some sort of validator, like this:
<input ng-model="other" ng-change="chg()"/>
Now, the trouble is, if I trigger the validator, $scope.chg(), from setFile() method, I think I don't get the same scope - chg() runs, but it's as if the validator is in another scope and doesn't set my actual submit button to enabled. I tried logging from the chg() - it shows different scope then what I actually see on the view.
And if I later trigger the ng-change by changing the regular input field ("other"), it picks up the changes, or actually, it sets the submit button state correctly.
Now, I suspect this has to do with me calling the angular.element(this).scope().setFile(this) from my form instead of direct, $scope-bound method. But I cannot call $scope-bound method because it does not trigger - if I understood correctly, that's due to Angular not (yet) working with input type=file fields.
What can I do here?
I simply want to detect if there is a file or not so I can enable/disable the submit button appropriately.
I used followed flow that works for me:
<input type="file"
ng-model="upFile"
onchange="angular.element(this).scope().setFileEventListener(this)"
/>
From controller:
$scope.setFileEventListener = function(element) {
$scope.uploadedFile = element.files[0];
if ($scope.uploadedFile) {
$scope.$apply(function() {
$scope.upload_button_state = true;
});
}
}
Hope it will help.

Disabling form password autocompletion but not other fields

I know you can disable the autocomplete on a form by setting autocomplete="off" on the form itself.
The problem I have is, I want to prevent the browser from populating the password field but do not want to disable username or other fields.
The other thing to consider is legacy data. Using autocomplete="off" on the form (or even the field itself) does not prevent existing users with saved passwords from getting a free-pass. Or ones that use web inspector, change the value of autocomplete and submit, allowing themselves to save the password.
I know it is possible to change the password field name attribute to a random/new one on every visit. Regretfully, I am working with a java/spring back-end and I am being told this is NOT easily manageable without a huge refactor/override.
How would you architect this? How would you enforce that the field always starts empty? There is no consistent way for browsers to event notify you of pre-population by a password manager - some may fire an onChange, others may not.
I guess I can move fields around with javascript and build the real form on the fly and submit it but once again, this will have implications with spring security and validations etc. Any other ideas?
you can made a temp variable when onFocus is call to set a variable to true ( like userFocus )
and on the onChange attribut but a short code for reseting "value" to NULL if userFocus== false) kind of overkilling imo but migth work
EDIT
function reset()
{
if (document.getElementById("hidden").value!=" ")
{
document.getElementById("demo").value=" ";
}
else;
}
function getfocus()
{
document.getElementById("hidden").value=" ";
}
else;
}
<input type="password" id="pwd" onchange="reset()" onfocus="getfocus()"/>
<input type="hidden" id="hidden" value="not focus"/>
I had to find this solution for IE 11 (since it ignores the autocomplete attribute). It works fine in other browsers. Really more of a work around, but it works.
https://stackoverflow.com/a/20809203/1248536
I was recently faced with this problem, and with no simple solution since my fields can be prepopulated, I wanted to share an elegant hack I came up with by setting password type in the ready event.
Don't declare your input field as type password when creating it, but add a ready event listener to add it for you:
function createSecretTextInput(name,parent){
var createInput = document.createElement("input");
createInput.setAttribute('name', name);
createInput.setAttribute('class', 'secretText');
createInput.setAttribute('id', name+'SecretText');
createInput.setAttribute('value', 'test1234');
if(parent==null)
document.body.appendChild(createInput);
else
document.getElementById(parent).appendChild(createInput);
$(function(){
document.getElementById(name+'SecretText').setAttribute('type', 'password');
});
};
createSecretTextInput('name', null);
http://jsfiddle.net/N9F4L/

Categories