Forms in angularJS (ngFormController)

Forms in angularJS are quite advanced then the normal HTML forms, and they work wonders with ngModelController. ngModelController, along with keeping the model and view in sync( by $formatters and $parsers) also helps in validating the data and checking if the value was changed from its default ( this is achieved by $pristine,$dirty,$valid,$invalid properties and by using the respective css classes) . There are a lot many properties and methods of ngModelController but we will mention these four for simplicity .

ngModelController.$pristine returns true if the value of the input field has not been changed from its initial state .

ngModelController.$dirty returns true if the value of the input field has been changed from its initial state .

ngModelController.$valid returns true if the value of the input field is valid (as the name suggests)

ngModelController.$invalid returns true if the value of the input field is invalid.

Now, a form directive (or ngForm) creates an instance of ngFormController . Each ngFormController does the same thing as ngModelController but for the whole form, instead of only an input element(in case of ngModelController) .

Now, how does it do it? Using the underlying ngModelControllers. Each ngModelController registers itself to its parent form, if there is any. The ngFormController also has the same properties and methods as ngModelController ( except $addControl(), $removeControl() , $setSubmitted() methods ) , so it sets its properties like $invalid,$valid,$pristine,$dirty according to the underlying ngModelControllers (like if any input ng-model field is set to $invalid, then the ngFormController is set to $invalid ) .

You should keep in mind , that any form elemnt creates an instance of ngModelController , like if you write like :
<form name="userForm">
</form>


ngFormController will be instantiated and css classes will be added to them instantly:

In developer tools , you will see

<form name="userForm" class="ng-pristine ng-valid">
</form


Now these properties of ngModelController and ngFormController can be used to do validation and other stuffs . Like , suppose you want to do the validation in a url input field , like show an error text if the url field is empty, and show an error text when the url is not a valid one. These can be very easy as ngModelControllers have built in validation for input types and you merely have to do this::

<form name="userForm" ng-controller="FormController">
<input type="url" name="url" ng-mode="url" required>
<span ng-show="Error(userForm.url, 'required')">url field should not be empty</span>
<span ng-show="Error(userForm.url,'url')">url is not valid</span>
</form>


and in the FormController:
.controller('FormController',function($scope){
$scope.url=' ';
$scope.Error=function(ngModelController,error){
return ngModelController.$error[error];
}
});


So this Error function takes in the ngModelController and the error type. So, how did we reference the ngModelController .
By the 'name' attribute we can reference the ngModelController. Syntax is : <name_of_the_form>.<name_of_the_directive_which_has_ng-model> .
And ngModelController has $error property , An object hash with all failing validator ids as keys.
In this case, the validator ids are 'required' and 'url' .

And you can check the validity of the whole form with "userForm.$error[]". Then we can disable the submit button, or disable a save button or any action which the user performs only when the form is valid, simply by calling a function which checks $valid property of the form .

<form name="userForm">
   <button ng-disabled="cannotSave()"></button>
</form>
.controller('FormController',function($scope){
  $scope.cannotSave=function(){
return $scope.userForm.$invalid ;
}
});


We have seen and understood the behaviour of form attribute in AngularJS , but what about ng-form, and why is it even there when we can achieve everything through form attribute . We missed out one thing, which cannot be achieved by form attributes :--' Nesting of forms' .
Using a form attribute inside another form attribute is invalid HTML , so AngularJS came up with ng-form . We can  nest any number of ng-forms inside form attributes and inside each other .

One very useful usecase is the the use of subforms as reusable components , which will reduce repeated code . Suppose we want to use the above url input form as a reusable component and embed it inside another form . Lets create a simple form which only has a FirstName and LastName input field .

<form name="parentForm">
<input type="text" name="firstname">
<input type="text" name="lastname">
<ng-include src="'urlform'"></ng-include>
</form>


we have included the form with ng-include . Now, the structure of the reusable form will be :

<script type="text/ng-template" id="urlform">
<ng-form name="userForm" ng-controller="FormController">
<input type="url" name="url" ng-model="url" required>
<span ng-show="Error(userForm.url, 'required')">url field should not be empty</span>
<span ng-show="Error(userForm.url,'url')">url is not valid</span>
</ng-form>
</script>


Notice that we have copied the whole code of the url form we had , and just replaced form with ng-form and it did the trick .

Also, we might have fields in a form which needs to be repeated and added dynamically, like the addresses of social networking sites you are active in, because someone has less number of  profiles and someone has more . We need to add fields with an 'Add' button , which will create an input field, and also we need to have validation in each of these fields.
Now, suppose we dont use ng-forms , then we will fully rely on ng-repeat directive , which will repeat the input fields . But each of this input fields dont have any identifier, like a name attribute or something , and we cannot generate  dynamically name attributes through angularJS for input directives .
What we can do in such situation is give an ng-form attribute in the directive which has ng-repeat
<div ng-repeat ="website in user.websites" ng-form="userForm">

Now, this will create a userForm for each each website in user.websites, and we can reference the ngModelController of the ng-model attributes inside this ng-form .

You can see this example from this link here .

So, this is what i had to discuss about Forms in AngularJS. Hope, you could make something out of it, and do give feedbacks .



3 comments:

  1. all is well and good, but I have a question...why the hell are you "MEAN-aakash". Is it your alter-ego or something?! o_O

    ReplyDelete
    Replies
    1. M-MongoDb, E-Express , A-AngularJS , N-NodeJS . Simple as that :)

      Delete