I'm stuck with strange problem regarding AngularJS form validation. If a dynamically added control (a textbox, for instance) requires validation and is removed from the form, the form will remain invalid if the removed control was invalid.
That last sentence is a bit confusing. See it in action with this plnkr preview (or see the plnkr editor).
I've checked the FormController API. Based on the documentation, there's no method to provoke any kind of form validation status refresh, although the AngularJS source code defines methods like $removeControl() and $setValidity() in the FormController.
Is there a standard way to circumvent the validation issue?
I came across this before, see this answer for more detail: https://stackoverflow.com/a/15192773/317180
Apparently this is an active bug.
A workaround is to provide a hidden counter that is incremented when elements are changed, this forces the form to revalidate:
In template:
<input type="hidden" ng-bind="abc" />
In controller:
$scope.remove = function(position) {
$scope.items.splice(position, 1);
$scope.abc += 1;
}
Related
I have a form where my intent is for required fields to not always be enforced. For example if the user is saving the document as a draft they can enter as little information as they like, if they try and publish the document then they have to enter all the required fields. I'm using a boolean on the controller which changes according to which button has been pressed e.g.
<input type="text" ng-model="field2" ng-required="enforceRequired" />
The problem is that the fields are not re-evaluated when the boolean changes so the form is submitted and then it becomes invalid. Please see this JSFiddle to see what I mean. If you fill in field1 and then click publish it will succeed on the first click and THEN become invalid.
How can I force the validation to run before the form is submitted?
Yarons is right, you are changing the value too late, by late I mean after the form validations has been run. What you can do as a workaround is, after changing the required value, let angular do another cycle and then show your alert. This can be done via $timeout service, although I must mention that it is usually not a good practise to change the flow of your digest actions. It gets pretty messy pretty soon.
Change your publish function like this (and don't forget to inject $timeout)
$scope.publish = function () {
$scope.enforceRequired = true;
$timeout(function () {
if ($scope.form.$valid) {
alert("Published!");
}
});
};
Working fiddle: http://jsfiddle.net/bh9q00Le/14/
The problem is that you are changing the value of enforceRequired in the middle of the digest loop, so the watchers are not re-rendered before you check the input fields' validity (read about digest here).
If you want to get around it, I suggest one of the following methods:
change the value of enforceRequired before you call saveDraft or publish. see example.
call $scope.$apply() after you change the value of enforceRequired. see another example.
I am trying to use the $invalid properties of ngForm to disable a submit button in my app. For some reason, the FormController instance that I should have access to after the <form> is created is not available. However, I moved the code over to jsFiddle and for some reason it works now.
Here is the jsFiddle
In my real application, there are no errors in the console and I am including angular.min.js correctly because everything else works fine. I am lost on what to try next since the code is basically the same in both.
Without seeing what is in your controller this is just a guess, but when I have problems with things not working for obscure reasons I generally am able to solve the problem by creating an object in scope and hanging everything off of it (much like you have already done.)
function Controller($scope) {
$scope.form = {
email:'',
employees:'',
migDate:''
};
}
The trick here is that you need to name your form similarly:
<form name="form.newMigForm" novalidate ng-controller="myControllerName">
and then when you check $invalid in your button, you use the same convention:
<input id="qaStartForm" ng-disabled="form.newMigForm.$invalid" ng-click="submitNewMigration()" type="button" class="btnOrangeLarge" value="Start" />
This problem generally arises when you use the UI Modal and they tell you to access it like this in the docs. I have used it successfully in other areas and solved the mystery that was plaguing me.
I'm building an Angular form with a URL field that can be formatted either with a prefix (http://) or without. Using Angular's default url validation requires http://, so I plugged in the following regex, which accepts with and without a prefix:
<input type="text" id="siteAddress" name="siteAddress" ng-model="user.url" ng-pattern="/^(https?:\/\/)?([\dA-Za-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/" required>
This works great, but when I set type="url", Angular's validation overwrites my custom ng-pattern. This form is for mobile users only, so the type="url" is important so that they receive the correct keyboard, and so their mobile OS does not attempt to autocorrect their input.
Is it possible to use type="url" without Angular applying its default validation, or is it possible to modify Angular's default validation so that it accepts no url prefix?
Thanks!
It seems like there is no official API to do that but you always can override default directive or use your directive with higher priority to change the standard behavior.
For example, angular's input directive gets the type from $attributes object, so you may remove this attribute.
directive('ignoreType', function() {
return {
priority: 500,
compile: function(el, attrs) {
attrs.$set('type',
null, //to delete type from attributes object
false //to preserve type attribute in DOM
);
}
}
});
Here is jsfiddle for it.
Of course this solution may also break behavior of other directives which work with type attribute, so use it carefully.
An idea would be to test the $removeControl method from the FormController to see if the native url validation would go off...
http://docs.angularjs.org/api/ng.directive:form.FormController
Inside your controller you would do something like this:
$scope.form.$removeControl('siteAddress');
Im not sure exactly what it will do, since I haven't got the chance to test it out and the docs about this are incomplete... let me know if this works please.
Hope that could give you some help.
When my field becomes invalid, is there a way to fire a method in my js?
So for example, the user fails to fill out the name field, clicks submit, I want to:
console.log('they forgot');
Thanks
There is a $error object made available by AngularJS. This object contains all of the validations on a particular form and tells if they are valid or invalid.
To get access to this property, we can use the following syntax:
formName.inputfieldName.$error
Here is a jsfiddle sample
This document is a good reference for understanding form-validations using AngularJS.
Also, for dealing with custom validations, you can add your own directives. A sample of making such directives is given in the link above.
I have been trying to build a form input directive which will generate a form input based on the model and the model attributes .
For example,
if the field is name and type is text, the directive will return a input html control,
if the field is a list, then it will return a select box
and so on
These inputs are generated using ng-repeat in the view. The inputs are bound to the model in the scope. This is working fine. However, the form validation fails; i.e if the input controls are invalid, the main form still shows the form is valid.
I have put up a simple plunkr to illustrate the issue - http://plnkr.co/edit/R3NTJK?p=preview
NOTE : I have actually nested the form, as the input name field is also dynamically generated from the scope model.
I have been trying to a get hold on this from the past 2 days and this is really driving me nuts.
I m not sure if I m missing something.
I would really appreciate if some one could help me out with this.
Update:
Use the following link function:
link: function linkFn(scope,elem,attr){
var jqLiteWrappedElement =
angular.element('<input type="url" name="socialUrl" ng-model="social.url">');
elem.replaceWith(jqLiteWrappedElement);
$compile(jqLiteWrappedElement)(scope);
}
Plunker.
For reasons I don't understand, the replaceWith() must be executed before the call to $compile. If someone can explain why this is so, we'd appreciate it!
Update2: in the comments below, Artem mentioned that the DOM must be modified before the linking function is called, so this also works:
var myElem = angular.element('some html');
var linkFn = $compile(myElem);
element.replaceWith(myElem);
linkFn(scope);
Original answer:
Instead of the link function, just use a template in your directive:
template: '<input type="url" name="socialUrl" ng-model="social.url">'