Angular, how to conditionally style a required field? - angularjs

I do not want to display validation of my form until the submit button is pressed. When the button is pressed the form should be submitted if valid, and if not submitted invalid fields should become highlighted in red.
For legacy reasons, I am importing a css file which defines input:invalid with a red background, as a result any input field marked as required will always display as red.
I attempt to make this work in the below fiddle by conditionally setting ng-required. Which somewhat works, with the exception that the first time the submit button is clicked the form $valid evaluates to true.
How can I conditionally set ng-required such that my field is only styled after the submit button has been clicked, and the form $valid consistently evaluates correctly based on the field being empty/non-empty?
https://jsfiddle.net/dk89dhp2/19/

You have two issues. The first is simply a typo.
this:
ng-required="myForm.showErrors"
should be:
ng-required="showErrors"
to match what's in the model.
Once you fix this, you will notice it almost works except the dialog says the form is valid when it should not. To give the digest cycle a chance to run, you can wrap whatever you need to do in a $timeout. (Could also use a proxy function that sets the value of showErrors then calls the submit function from a $timeout)
$scope.myForm.submit = function() {
$scope.showErrors = true;
$timeout(function() {
alert("form validity is: " + $scope.myFormNg.$valid);
});
};
updated jsfiddle

This works:
https://jsfiddle.net/q6ur26mt/
I just changed the ng-required value to true.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></script>
<body ng-app="myapp">
<div ng-controller="MyController" >
<form name="myFormNg">
<input type="text" ng-class="myForm.getCssClassForField(myFormNg.id)" ng-required="true"
name="id" ng-model="myForm.id" />
ID
<br/><br/>
<button type="button" ng-click="myForm.submit()">submit</button>
</form>
</div>
</body>
<script>
angular.module("myapp", [])
.controller("MyController", function($scope) {
$scope.myForm = {};
$scope.showErrors = false;
$scope.myForm.submit = function() {
$scope.showErrors = true;
alert("form validity is: " + $scope.myFormNg.$valid);
};
$scope.myForm.getCssClassForField = function(field) {
return field.$invalid && $scope.showErrors ? 'invalid' : '';
};
});
</script>

Related

angular form how to show changed save button text?

I use angular 1 form to show changed save button text.
The button text should be the follow:
(disabled) save - when initial
(enable) save - when edited or re-edit after saved
(enable) saved - click save button
How can I use a simple method to resolve it?
sample JSFiddle here or the follow code
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function($scope) {
$scope.buttonText = 'save';
$scope.save = function (myForm) {
myForm.$setPristine();
$scope.buttonText = 'saved';
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="MyCtrl">
<form name="myForm">
<input type="text" ng-model="text1" name="text1">
<input type="text" ng-model="text2" name="text2">
<button ng-click="save(myForm)" ng-disabled="myForm.$pristine">
{{buttonText}}
</button>
</form>
</div>
</body>
Change your button markup to this:
<button ng-click="save(myForm)" ng-disabled="myForm.$pristine">
{{myForm.$pristine?buttonText:'save'}}
</button>
Working fiddle
You could attach a watch to the form model object and set the text back to 'Save'?
Asides from that you already have it with the initial value and setting to Saved when button is clicked
$scope.$watch("myFormData", function(){
// text is 'Save'
}, true);
For the following to work, put all your form data in an object variable.
I.e.
myFormData = {};
myFormData.text1 = "hi"
So you can change the ng-model binding to a viewmodel - which is good practice anyway!

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 form validation - at least one input with value - add class

I have a form where at least one input must contain data, so I'm using the following to disable the submit until at least one input has data:
<form novalidate>
<input type="text" ng-model="caption.topCap" id="topCap" class="pull-left" required>
<input type="text" ng-model="caption.bottomCap" id="bottomCap">
<input type="submit" value="Caption It" class="btn btn-block btn-success btn-lg" ng-disabled="!(caption.topCap || caption.bottomCap)">
</form>
This is the controller:
capApp.controller('captionFormController', ['$scope', function($scope) {
$scope.master = { topCap: "Top Line", bottomCap: "Bottom Line" };
$scope.update = function(caption) {
$scope.master = angular.copy(caption);
};
$scope.reset = function() {
$scope.caption = angular.copy($scope.master);
};
$scope.reset();
}]);
The problem is that I cannot use required on the fields because only one or the other is required, therefore I never get a class of ng-invalid added to the input, so although both fields might be empty, and I have a disabled submit button, I want to have a class added if both fields are empty.
Is there a way to have a class added to the fields, if they have been touched but are both empty? They are prefilled on page load by the way.
ng-required allows you to use an expression to set if the field should be marked as required or not, you could make the fields reference each other values there.
For details and a very nice working example see the response for the following question: How can I conditionally require form inputs with AngularJS?
could probably use ng-required
input(ng-required="!caption.bottomCap, ng-model="caption.topCap")
input(ng-required="!caption.topCap, ng-model="caption.bottomCap")

unable to set validity on form

I am trying to set a validation token "fooname" for a form through its controller, on the click of a button based on a condition.
However it looks like form.$error.fooname is not getting set. {{form.$error.fooname}} is parsed as null.
javascript:
angular.module('app', [])
.controller('controller', ['$scope', function ($scope) {
$scope.data={};
$scope.data.validate=function () {
if ($scope.data.name=="foo") {
$scope.form.$setValidity("fooname",true);
}
};
}]);
html:
<form name="form" ng-controller="controller"
ng-submit="data.validate()" ng-init="form.$setValidity('fooname',false)"
novalidate>
<input type="text" ng-model="data.name" name="name"/>
<button type="submit">Submit</button><br/>
{{data}}<br/>
{{form.$error.fooname}}
</form>
The $setValidity function defined on form controller, takes three parameter as per the source code:
form.$setValidity = function(validationToken, isValid, control) {
Also to fail a validation you need to set the value to false not true. Change your expression to
$scope.form.$setValidity("fooname",true,$scope.form);
Also the $error for FormController returns the list of controllers that are failing the validation, so you need to adjust you code accordingly.

Angular.js - ng-change not firing when ng-pattern is $invalid

I am using ng-pattern to validate some form fields, and I am using ng-change with it to watch and process any changes, however ng-change (or $scope.$watch) will only fire when the form element is in the $valid state! I'm new to angular, so I don't know how to solve this issue, although I suspect a new directive is the way to go.
How can I get ng-change to fire in both $invalid and $valid form element states, with ng-pattern still setting the form element states as before?
Html:
<div ng-app="test">
<div ng-controller="controller">
<form name="form">
<input type="text" name="textbox" ng-pattern="/^[0-9]+$/" ng-change="change()" ng-model="inputtext"> Changes: {{ changes }}
</form>
<br>
Type in any amount of numbers, and changes should increment.
<br><br>
Now enter anything that isn't a number, and changes will stop incrementing. When the form is in the $invalid state, ng-change doesn't fire.
<br><br>
Now remove all characters that aren't numbers. It will increment like normal again. When the form is in the $valid state, ng-change will fire.
<br><br>
I would like ng-change to fire even when the the form is $invalid.
<br><br>
form.$valid: <font color="red">{{ form.$valid }}</font>
</div>
</div>
Javascript:
angular.module('test', []).controller('controller', function ($scope) {
$scope.changes = 0;
$scope.change = function () {
$scope.changes += 1;
};
});
I have created a working JS Fiddle which shows the problem I am having.
http://jsfiddle.net/JAN3x/1/
By the way, this angular issue also seems to be relevant:
https://github.com/angular/angular.js/issues/1296
You can change the behavior of your input by using ng-model-options.
Just add this attribute to your input and the ng-change event will fire:
ng-model-options="{allowInvalid: true}"
see: https://docs.angularjs.org/api/ng/directive/ngModelOptions
you just need to add
ng-model-options="{ updateOn: 'default' , allowInvalid:'true'}"
this indicates that the model can be set with values that did not validate correctly instead of the default behaviour.
Edit This was answered when ng-model-options was not available. Please see the top-voted answer.
you can write a simple directive to listen input event.
HTML:
<input type="text" name="textbox" ng-pattern="/^[0-9]+$/" watch-change="change()" ng-model="inputtext"> Changes: {{ changes }}
JS:
app.directive('watchChange', function() {
return {
scope: {
onchange: '&watchChange'
},
link: function(scope, element, attrs) {
element.on('input', function() {
scope.$apply(function () {
scope.onchange();
});
});
}
};
});
http://jsfiddle.net/H2EAB/
Inspired by the Li Yin Kong ingenious solution :
His solution has an issue concerning the ndModel update (see the comments of his post).
My fix essentially changes the scope type of the directive. It lets directive access to controller scope (and methods)
Then, watch-change directive does not need an "instruction to eval" (change()) anymore, but only the "name of the controller method to call" (change).
And to get the new value of the input in this function, I pass the context (this = the input itself). So I can get the value or any property of it.
This way, we don't care about ngModel updates (or if the form is invalid, which was another issue of the initial solution : ngModel is deleted if form is invalid)
HTML :
<input type="text" name="textbox" ng-pattern="/^[0-9]+$/" watch-change="change" ng-model="inputtext">
JAVASCRIPT :
app.directive('watchChange', function() {
return {
restrict : 'A',
link: function(scope, element, attrs) {
element.on('input', function(){
scope[attrs.watchChange](this);
})
}
};
});
DEMO : http://jsfiddle.net/msieurtoph/0Ld5p2t4/

Resources