Angular doesn't restore null-but-valid input value on model change when input is invalid - angularjs

I have run across a weird/confusing behavior with <input> and form validation.
Essentially, if input has an invalid numeric value (which sets form.$valid to false), if a model value is changed to null, it sets the $valid flag back to true, but doesn't change the value displayed in the input field.
I have created a plunker to illustrate. Follow these steps to reproduce:
Modify value of a cell to empty.
Click on save link (which saves null into model)
Input a non-numeric character.
Click on reset link (which restores null from model)
Observe that the input field is no longer invalid, but the invalid character is still there.
Is this a bug or am I doing something wrong?
EDIT:
I'm starting to believe that it is a bug. I "fixed" it by introducing another directive that forces "" value for null.
Here's a fork of the plunker above with the "fix".
EDIT2:
This was fixed in v1.3+

You code is working fine but you forgot to add a validation check at app.js
this.save = function(){
if($scope.mainForm.$valid && !this.get() == ""){
old_num = num;
}
};
You should only save the value when the form is valid.
However that is not a full prove solution as the entire form has to be valid in order for save to work. To further improve the algorithm, i suggest that you mark the validity of individual model and you check validity to the individual model.
Hope it helps

Had a similar problem, I solved it by first doing:
form.$rollbackViewValue() or form.field.$rollbackViewValue if it was .$invalid before doing any of my other reset code.
In your example, you could change to something like:
<button class="btn btn-link" ng-click="cellForm.$rollbackViewValue(); item[prop].reset()">reset</button>

Related

AngularJS: invalid input is not styled as invalid until I click on the input field and click out of it

I am setting input field to invalid from javascript like this:
$scope.formName.fieldName.$setValidity("string", false);
This is working, but it affects the view only after clicking in a input field and then clicking out of it.
How can I change it in order to see the invalid field immediatelly?
You'll need to attach an ng-change="checker(model)" to your input field. Then just apply an ng-class to it to check for formName.fieldName.$error.string (since you put string as the error key) or you can also do formName.fieldName.$invalid && !formName.fieldName.$pristine
I've attached this codepen for you to play around with and see how to apply an invalid class on the field without having to lose focus on it.
Hope that helps!

how to make angular-xeditable edit state always enabled

I am using angular-xeditable to edit elements within a form. I would like to have all the elements in "editable" mode at all times, e.g. no "edit" button needs to be pushed before the user starts editing.
I have been trying to use the $show() in my controller to enable the elements in the form, however it seems like the elements goes into viewing state again for example when using "onaftersave" when trying to save the values etc.
How do I do in order to always be in edit mode where the user never needs to enable editing in order to start editing the values?
Possibly try adding shown=true in your html:
<form editable-form shown="true">
I had this same problem. Even when trying to $show() the form again at the end of my onaftersave function it still wouldn't work. I suspect the visibility is being hidden after the onaftersave function runs but I didn't actually check if that's the case. Here's how I got around it:
$scope.$watch('yourFormName.$visible', function() {
$scope.yourFormName.$show();
});
Now whenever the form visibility changes to hidden, it will just show it again regardless of when it changed.
If your form is already open, and you just want to keep it open after submitting, you can do this:
<form editable-form name="MyForm" onbeforesave="saveMyData()" onaftersave="keepFormOpen()">
and the function keepFormOpen() would look something like this:
$scope.keepFormOpen = function() {
return "fake error message";
}
What this does is essentially gives the form a fake error message after you have already saved your data. This fake error message will interrupt the forms closing process, but since it was called after you already saved your data, it won't affect the previous submit.
To really keep open the form we can do the following:
<form editable-form name="MyForm" onbeforesave="saveMyData()" onhide="MyForm.$show()">

ng-maxlength preventing a valid value from being databound to input field

I have the following field in a form:
<input type="text" name="dedicatedstaff" ng-model="staffingRecord.dedicatedStaff"
tabindex="9" ng-pattern="/^[0-9]{0,4}(\.[0-9]{1,2})?$/" ng-maxlength="7" />
The form is to edit an existing record. No matter what value is in the existing record, the field fails validation and the databind becomes undefined. Some example values that exist on the records are 1, 2.5, 12.5, 99.25, 4.0. I believe every one of these should pass both the pattern and maxlength validations, but it isn't working. I've checked the model and the values are present when loading the form.
When I remove the ng-maxlength directive and just have the ng-pattern, it works fine and those values pass validation. If I remove ng-pattern and just have max-length, it fails. It also doesn't matter if the INPUT is of type text or number. If ng-maxlength is present, it fails. Browser also does not make a difference (tested Chrome, IE & Firefox). I have also verified that it is the maxlength error in the error list.
I am also using ng-maxlength with almost every other field on this particular form, and they also work just fine. And if I type the exact values listed above after form load when ng-maxlength is present validates fine at that point. But that's not a reasonable workflow to make the client type the values over again every time they load the form.
I don't understand it as I use this same pattern in other forms within the app and they work fine. I can get by with just ng-pattern on this particular field, but I would much rather figure out why, in this one case, it won't validate properly on load.
I'm using AngularJS 1.2.14, with JQuery 1.9.1.
I figured it out. It was actually the INPUT type after all. After further testing, I realized my initial test of that variation was incorrect. Changing the INPUT type to NUMBER fixed the validation issues.
<input type="number" name="dedicatedstaff" ng-model="staffingRecord.dedicatedStaff"
tabindex="9" ng-pattern="/^[0-9]{0,4}(\.[0-9]{1,2})?$/" ng-maxlength="7" />

AngularJS ng-required allowing only whitespace

I have an input[type=text] on my form with an ng-required="true".
I have a button click event that updates the model that the input is bound to.
If the updated value is blank, the form becomes invalid.
But If the updated value is entirely whitespace, the form is valid.
If you delete the whitespace and readd it, the form becomes invalid again.
This jsfiddle demonstrates: http://jsfiddle.net/dayeh/
Is there a workaround to this issue? For now I will just trim all my back-end data.
The problem occurs because of the way ng-model works.
By default ng-model trims input values. If you look at this modified fiddle with quotes around the value you can see that user inputed spaces show up as an empty string.
However if you force the model to be an empty space the trimming is not applied and therefor the model is technically valid.
You can remove the default trimming of ng-model with ng-trim="false". Then you'd get the more expected behavior.
After clicking on you button, the value gets resets. However, the value is being reset to . An empty whitespace. If I set it to $scope.myData = '';, then valid is false (as expected). If I enter whitespace in manually, then valid remains to false.
So I assume $scope.myData = ' '; is the problem that you're having. Switch it to scope.myData = '';
Updated fiddle.

How can I do validation and use ..$setPristine(); in an AngularJS form?

I have the following code:
<form class="form"
data-ng-submit="modalSubmit(modal.data)"
id="modal-body"
name="modalForm"
novalidate>
This works and when I click on a button of type submit then the modalSubmit function is called.
However I would like to do this in my controller:
$scope.modalForm.$setPristine();
But it gives an error saying:
has no method '$setPristine'
How I can I set the form to pristine? I did try adding data-ng-form="modalForm" but then I get
a message saying something to the effect of duplicate directive names.
I tried changing the form element to a DIV but then the clicking on the submit button does not call
the function
Here's an example (modified from another user) that shows what I am trying to do which is set values to pristine:
plnkr.co/edit/LNanJdAggMLIgxii0cfv?p=preview
You're not doing anything wrong there, only problem is you're referencing an old version of angular in which $setPristine() was not a feature. $setPristine() was added in 1.1.+, so reference a newer version of angular and you're good to go. See it working in this plunk, using 1.2.+.
If you can't upgrade, then a dirty workaround would be to loop through all inputs in the form and set their $dirty and $pristine values manually:
$scope.mp = function() {
$scope.mainForm.$pristine=true;//clean main form
$scope.mainForm.$dirty=false;
angular.forEach($scope.mainForm,function(input){//clean all input controls
if (input !== undefined && input.$dirty !== undefined) {
input.$dirty=false;
input.$pristine=true;
}
});
}
First, your version of angular was old, 1.2.12 is the latest stable on the CDN. But even it wouldn't allow $setPristine because of the HTML5 validation that was going on.
The biggest problem was you used required on the fields instead of ng-required. The browser was doing the form validation for you instead of angular. You could also add the novalidate attribute to the form tag.
http://plnkr.co/edit/l1mUCceSFMFFZWgGgL6u?p=preview
it has already been implemented in this link you can use it this was as it has been demonstrated in the plnkr link.
As you can see from the above description, $setPristine only changes the state of the form (and thereby resets the css applied to each control in the form).
If you want to clear the values of each control, then you need to do for each in code.

Resources