Don't record invalid values with ng-model - angularjs

I really like how the ng-model attribute binds directly to my model and users get instant feedback on their changes. For my use case that's perfect. However, I don't want invalid values to be put into the model where they can throw a wrench into the calculations. I somehow want the model to only be updated if the value in the form control is valid. For invalid values, it's fine for the control value to change while the model value stays fixed.
If I change the source of angular (1.2rc) NgModelController's $setViewValue implementation:
this.$setViewValue = function(value) {
...
if (this.$modelValue !== value) {
this.$modelValue = value;
...
}
};
To this:
this.$setViewValue = function(value) {
...
if (this.$modelValue !== value && this.$valid) {
this.$modelValue = value;
...
}
};
It seems to do exactly what I want, however I don't know how to do this in a proper way. What's the right way to change this behavior? Or are my attempts doomed to failure for some reason?
Update: Added example.
For example look at http://jsfiddle.net/FJvgK/1/ HTML:
<div ng-controller="MyCtrl">
{{validNumber}}
<form>
<input
type="number"
ng-model="validNumber"
required
min="10"
max="20"
/>
</form>
</div>
And the JS:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.validNumber = 15;
}
The number shows properly for values between 10 and 20, but I want it so that if you suddenly type '8' into the box, or delete the second digit leaving '1' the last valid number still shows above. That is, the model always has a valid value, even if the control does not.

I believe the default behaviour of AnugularJS validators are not to update the model if the value passed is invalid. If you look at the developer guide and go through Custom Validation these samples also show that the model is not update or is cleared on invalid value provided in the UI

This is default behaviour, but, you can modify this using ngModelOptions directive
<input
type="number"
ng-model="validNumber"
required
min="10"
max="20"
ng-model-options="{ allowInvalid: true }"
/>
Documentation: https://docs.angularjs.org/api/ng/directive/ngModelOptions See the section 'Model updates and validation'

As Chandermani said, it is the default behavior, here is a example that shows it in action :
<form name="myform">
<input type="text" name="myinput" ng-model="myvalue" ng-minlength="4" required>
</form>
Is the input valid ? {{ myform.myinput.$valid }} <br />
Input's value : {{ myvalue }}
{{ myvalue }} doesn't show anything until you write at least 4 characters in the input.
Best Regards.
EDIT
If you need a default value, I guess you could break down your value into 2 values, using a computed value :
var validNumber = 15;
$scope.validNumber = function () {
if ($scope.myform.myNumber.$valid) return $scope.myNumber;
else return validNumber;
};
I set up an example here : http://jsfiddle.net/7vtew/1/

Related

Angularjs $viewValue.length for form validation

I have a simple from to which I am trying to add validation. I am comparing the $viewValue in the form with the value of ng-min/mg-max to notify the user if he is trying to enter values that are over the max and under the min allowed. The issue is that if I use ng-if="myform.$viewValue.length > maxvalue" the error does not appear. However, the error DOES appear when I exclude .length() However, in this case it is checking every integer in of $viewValue string with maxvalue number, which is not intended. I understand that $viewValue.length would give me a number instead of a string so that I may compare a number with a number, but in this case the error never appears.
I even have a JSFiddle example that works https://jsfiddle.net/up2qxcxe/
But it does not in my app.
Here is my form:
<form name="modelParamsForm" novalidate>
<div class ="row">
<div class="form-group col-md-4" ng-repeat="modelParam in
modelParams" ng-class="{ 'has-error' :
modelParamsFieldForm.value.$invalid }">
<ng-form name="modelParamsFieldForm">
<label form-group>{{modelParam.label}}</label>
<input type="number" class="form-control input-sm"
ng-model="modelParam.value" name="value"
ng-min="modelParam.minvalue" ng-max="modelParam.maxvalue" >
<span ng-show="modelParamsFieldForm.value.$invalid"></span>
//MIN-MAX VALIDATOR
<div ng-if="modelParamsFieldForm.value.$viewValue.length >
modelParam.maxvalue">Max exceeded:{{modelParam.maxvalue}}
</div>
</ng-form>
</div>
</div>
My controller is nothing special:
$scope.modelParams = {};
$scope.loadModelParams = function(modelName) {
var req = {
method : 'GET',
url : urlPrefix + 'PB_OPTIMISER_GET_MODEL_PARAMS',
params: {modelName : modelName}
};
$scope.loading = true;
$http(req).then(function(response) {
$scope.loading = false;
$scope.modelParams = response.data;
$scope.modelParamsOld = angular.copy($scope.modelParams);
}, function() {
$scope.loading = false;
});
};
EDIT: In my JSON modelParam.maxvalue is a string. I think the problem was that I am comparing a myform.$viewValue.length with modelParam.maxvalue which is string type. I am not sure how to get around this...do I have to parse maxvalue?
You do not need $viewValue. Your ng-ifs are wrong. Change to
<input type="number" class="form-control input-sm col-md-5" ng-model="modelParam.value" name="test" min="modelParam.minvalue" max="modelParam.maxvalue">
<span ng-if="modelParam.value > modelParam.maxvalue">Too Many!!</span><br>
<span ng-if="modelParam.value < modelParam.minvalue">Too Few!!</span>
It should work fine. Here is your Plunker working the way I think you want.
The problem here is that the modelParam.maxvalue was coming from the database as a string, rather than integer. So when comparing ng-if="myform.$viewValue.length > modelParam.maxvalue" I was comparing an integer with a string, and that obviously did not produce the desired results. The solution was to go to the table and make maxvalue an integer.
Alternatively one can have a second variable for maxvalue in the controller that parses the string like so: $scope.mpMaxValue = parseInt($scope.modelParams.maxValue)
See this post for a longer discussion: AngularJS comparing $viewValue against ng-model or object param

ng-change triggered before value is updated

I have a form which needs to save data each time something is changed. I've used ng-change on all form elements to trigger a form validation and a save. However in case of radio buttons, ng-change is triggered before the actual value is updated, thus resulting in an invalid form on the first try, and an outdated form all subsequent times.
I've set up a JSFiddle to illustrate this. The console prints out whether the form is valid or not. The same applies if I were to print the value of $scope.form.test.$modelValue.
// HTML
<div ng-controller="MyCtrl">
<form name="form">
<input type="radio" name="test" ng-model="test" value="yes" required ng-change="checkRadios()" /> Yes<br/>
<input type="radio" name="test" ng-model="test" value="no" required ng-change="checkRadios()"/> No
</form>
</div>
// JS
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.test = null;
$scope.checkRadios = function(){
console.log($scope.form.test.$modelValue);
}
}
Is my logic faulty, is this a valid bug, or does it work as expected? In the last case, what can I do to always get the actual value?
You need a delay to get the updated value of the $scope.form, so it is possible to achieve by using $timeout
http://jsfiddle.net/loen22/w7dpx57f/
$scope.checkRadios = function(){
$timeout(function () {
console.log($scope.form.$valid);
});
}

angularjs object changes affects other object

I have AngularJS application. From server I am getting a JSON. I am assigning to two objects. One I am using in my form page to edit. When the user press submit, I want to check whether the new value and old are same. But whenever I update the value in form and checks, the other object value also gets updated., resulting both object becomes same always.
Here is the JSFiddle Demo. Whenever I am trying to change the value, the other value also gets updated.
What is the solution to prevent the other object to not change?
HTML
<div ng-app>
<h2>Form</h2>
<div ng-controller="MainCtrl">
<form name="myForm" ng-submit="saveForm()">
<input type="text" ng-model="obj.name">
<input type="text" ng-model="obj.value">
<input class="btn-primary" type="submit" value="Save" ng-disabled="myForm.$pristine">
</form>
</div>
</div>
JS
function MainCtrl($scope) {
$scope.obj={
name:"N1",
value:"val"};
var oldObj = $scope.obj;
$scope.saveForm=function() {
alert(JSON.stringify($scope.obj));
alert(JSON.stringify(oldObj));
if($scope.obj===oldObj){
alert("same");
} else {
alert("not same");
}
}
}
The problem is, that you just store an reference to oldObj.
So oldObj and the $scope.obj is always the same.
You need to copy it.
Check the fiddle: http://jsfiddle.net/fc0jo1so/1/
I did it with fromJson, toJson
You just change this line
var oldObj = angular.copy($scope.obj);
Try it will work. & give alert not same
Try using jQuery to make a deep copy
var oldObj = jQuery.extend(true, {}, $scope.obj);

angular-kendo UI, NumericTextBox, cant display zero value

When I set initial value of variable to "0", then NumericTextBox field cant show this value.
Code:
<div ng-app="app">
<div ng-controller="MyCtrl">
<input type="text" ng-model="value" kendo-numeric-text-box>
<br>
<input type="text" ng-model="value">
</div>
</div>
JS:
angular.module('app', ['kendo.directives']);
function MyCtrl($scope) {
$scope.value=0;
};
In jsfiddle you can see that simple text field displays zero value, but kendo NumericTextBox is empty. But if you type zero again in simple text field, then zero will be in kendo field too.
I think this is a bug, how to get around this problem?
Well, if it is a bug, you can either ask the maintainer of angular-kendo to fix it, or you can fix it yourself. It's a relatively simple fix, I think, because the link function doesn't distinguish between model values 0 and undefined. So you'd need to make sure the widget's value method is only called with null if the value is actually null or undefined:
if (typeof ngModel.$viewValue !== 'undefined') {
widget.value(ngModel.$viewValue);
} else {
widget.value(null);
}
Here's a demo with a fixed version (links to a fork of angular-kendo, mind you).

Get "raw" value from invalid input field

I have an input field in a form with some validations. It works like a charm.
It basically looks like this:
<input
class="form-control"
type="number"
ng-model="zipcode"
ng-minlength="5"
ng-maxlength="5"
id="zipcode"
name="zipcode"
required
>
A working plunkr is here: http://plnkr.co/edit/H0h59kG75T5MGE9cAhSo?p=preview
But now I also want to react to every input change - whether valid or not. So for example if the input field contains "123" it is not valid and the value is not transferred to my model - thats fine. But I still want to get the value to do some intermediate requests to a webservice.
Any Ideas?
First call the form element in your controller, then use the $viewValue attribute :
View :
<form name="form">
<input
...
ng-model="zipcode"
ng-change="getRawValue(form)"
name="zipcode"
required
>
</form>
Controller:
$scope.getRawValue = function(form) {
var rawValue = form.zipcode.$viewValue;
}
Angular 1.3 introduced a real answer for this: allowInvalid in ngModelOptions.
Example:
<input
type="text"
name="userName"
ng-model="user.name"
ng-model-options="{allowInvalid: true}"
>
Here is what i came up with for your scenario.
Basically you can write a directive which requires ngModel (ngModelController). The ngModelController has a array of parsers which it call to parse the view value in a pipeline manner. If validation fail these parsers do not update the model. If you inject a custom parser at the start of this parsers array, you can get the each view change value and do anything you want with it.
See my plunkr here http://plnkr.co/edit/ruB42xHWj7dBxe885OGy?p=preview (See console)
The basic code would be
ngModelCtrl.$parsers.splice(0,0,(function (viewValue) {
console.log("The view value is:"+viewValue)
return viewValue;
}));
Also see ngModelController documenation

Resources