Angular Template Driven Forms - Part Seven

Here we’ll start learning building forms in Angular. At the end of this article, we’ll be able to implement forms with different kinds of input fields, such as textboxes, checkboxes, radio buttons, and dropdown lists. We’ll be able to display the validation message next to input fields and disable the "Submit" button if the form isn’t in a valid state. But if you’ve not read my previous Angular articles, then you can start your journey from here.

Now, let’s get started.

Building a Bootstrap Form

So now, we’ll build the bootstrap and then we’ll apply the validations upon the form. So, let’s open the terminal and generate a new component.

PS C:\Users\Ami Jan\HelloWorld\MyFirstAngularProject> ng g c contact-form

Go to contact-form.component.html and replace the HTML script.

  1. <form>  
  2.   <div class="form-group">  
  3.       <label for="firstName">FirstName</label>  
  4.       <input id="firstName" type="text" class="form-control">  
  5.   </div>  
  6.   <div class="form-group">  
  7.     <label for="comment">Comment</label>  
  8.     <textarea id="comment" cols="30" rows="10" class="form-control"></textarea>  
  9.   </div>  
  10.   <button class="btn btn-primary">Submit</button>  
  11. </form>  

We can generate this HTML through Zen Coding. If you don’t know about Zen Coding, have a look here.

Label[for=’firstName’]+input[id=’firstName’][type=’text’].form-control Press TAB

It will automatically generate your HTML. But the Bootstrap requirement is that the elements should have in the form-group div. So,

div.form-group>label[for=’comment’]+textarea[id=’comment’].form-control  and press TAB

It will generate your 2nd div and enclose your 1st div elements in form-group div manually. Now, it is the time to generate the button with Zen Coding.

button.btn.btn-primary Press TAB

This is how we use Zen coding.

Now, open contact-form.component.ts and change the name of the selector.

  1. @Component({  
  2.   selector: 'contact-form',  
  3.   templateUrl: './contact-form.component.html',  
  4.   styleUrls: ['./contact-form.component.css']  
  5. })  

Now, open app.component.html and remove the existing text and write the contact-form HTML element.

  1. <contact-form></contact-form>  

Save all the files and run the application by executing the following command in terminal.

PS C:\Users\Ami Jan\HelloWorld\MyFirstAngularProject> ng serve

And have a look at how our form looks in the browser.

form in the browser
 

Types of Forms

So, we’ve built this simple form and now we need to add the validations to it. In Angular, we have a class called FormControl for each input field in our form. We need to create an instance of the Control class. With this instance of Control class, we can check the value stored in the input field. We can see if the input field has been touched or untouched; we can see if it is dirty (means its value is changed) or we can check if the control is pristine (means value is not changed); whether it is valid or not and if it is not, what are the validation errors. So, for each input field in the form, we need a control object.

Types of Forms

Similar to the control class, we’ve another class called FormGroup which represents a group of controls in a form. Each form is essentially a control group because it contains at least 1 control but in the complex application, you might have a form with multiple control groups i.e. you might have a control group for the shipping address and another for the billing address. And interestingly, all the properties which you’ve seen above with FormControl are also available with FormGroup.

We can ask the FormGroup if it is valid or not and that returns true if all the controls in that group are valid. Similarly, we can get all the validation errors in that group. So, accessing these properties of our FormGroup is easier than iterating over all the controls in that group and checking their status.
Types of Forms

So, to add validation to the form, we need to create the FormGroup object for the form and FormControl object for each input field of the form. In this way, we can keep track of the state of each input field and the entire form as a whole.

Types of Forms

Now there are 2 ways to create these control objects. One way is by applying some directive in our template and Angular will create these control objects for us implicitly under the hood. We call form that is built in this way Template-driven forms. So we build them using the template. Another approach explicitly creates these control objects, so in our components, we should write the code to create a new instance of the control group and control objects. We call forms that are built in this way Reactive form. Previously, they used to be called Model-driven forms but now, they’re called Reactive form.

Types of Forms

Now you might ask what is the difference between template driven forms and reactive forms. The difference is that when we create the control objects explicitly we have more control over validation logic. And it is good for complex forms and also another benefit of this approach is that we can unit test the validation logic of our form. Otherwise, if we want simple forms where we want some basic validation like making something required, or setting the range of the values of the input fields, then we can use template-driven forms. Those are easier to create and we write less code. Here we’ve less control over the validation of our form.

Types of Forms

ngModel

So, we’ve built this basic bootstrap form. Now, we want to add validation to this using a template-driven approach. As we discussed above, with the template-driven approach, we apply the directive to our input field and Angular will create a control object associated with that input field under the hood. We already know that directive ngModel, we were using this directive for the 2-way binding purpose. But here, we’re using the ngModel directive in the form without any binding. Angular will create the control object under the hood.

  1. <div class="form-group">  
  2.     <label for="firstName">FirstName</label>  
  3.     <input ngModel id="firstName" type="text" class="form-control">  
  4. </div>  

Now, let’s preview it in the browser.

ngModel

And in Chrome, we get this error “if ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as ‘stadalone’ in ngModelOptions.” So basically, the reason we got that error is that we didn’t set the name attribute in the input textbox, this is the requirement. Because every time we apply the ngModel on an input field and it needs the way to distinguish these control objects. And here, we set the name attribute.

  1. <div class="form-group">  
  2.     <label for="firstName">FirstName</label>  
  3.     <input ngModel name="firstName" id="firstName" type="text" class="form-control">  
  4. </div>  

And now, the error is gone. So, you can see we use ngModel with name attribute to implement template driven approach. But let’s explore what is happening under the hood. I want to handle the change event of this input field and here, we will call the log method and here, we need the reference of ngModel directive and we want to pass the reference to the log method. So, we can log on the console. For that let us create the template variable.

#firstName=""

Here, we can call anything in the input id.

  1. <input ngModel name="firstName" #firstName="ngModel" id="firstName" (change)="log()" type="text" class="form-control">  

So when Angular sees this, it sets the template variable to the ngModel directive which is applied on this input field. Then, we pass this method to our log method.

  1. <input ngModel name="firstName" #firstName="ngModel" id="firstName" (change)="log(firstName)" type="text" class="form-control">  

Now, let’s go and implement this method. So, back in our component -

  1. import { Component } from '@angular/core';  
  2.   
  3. @Component({  
  4.   selector: 'contact-form',  
  5.   templateUrl: './contact-form.component.html',  
  6.   styleUrls: ['./contact-form.component.css']  
  7. })  
  8. export class ContactFormComponent {  
  9.   log(x) {  
  10.     console.log(x);  
  11.   }  
  12. }  

Now, back in the browser, give some value in the firstName textbox and press tab or focus out from the textbox with the help of mouse by clicking somewhere else in the browser. You’ll see the ngModel object in the console.

ngModel object in console

You can see the property control in the ngModel object, so this is the property we’re discussing above. And you can see this control property itself is an instance of FormControl in Angular. And we’ve discussed the properties of FormControl class above. Now if you expand it more here then you’ll see those properties here.

properties

So if you see the properties with full attention. Dirty is true because I’ve typed my name and dirty property is the property in which the value is changed. Now the opposite property is pristine which is false. We’ve another pair which is invalid and valid, in this case, because we haven’t implemented validation yet so this input field is valid and invalid is false and valid is true. And if we’ve any validation error, they will be available in error property which is null right now. We’ve another pair which is touched and untouched. In this case, because I’ve touched the input field, or in other words, I put the focus there and then moved away touched should be true and untouched should be false. Value property returns the current value of the input textbox.

So what we note here is that we use this FormControl class to track state changes and the validity of the input fields. When we apply the ngModel directive along with the name attribute in the input field. Angular automatically creates an instance of FormControl class and associated with this input field. Now let’s attach ngModel with name attribute with other input elements as well.

  1. <form>  
  2.   <div class="form-group">  
  3.       <label for="firstName">FirstName</label>  
  4.       <input ngModel name="firstName" #firstName="ngModel" id="firstName" (change)="log(firstName)" type="text" class="form-control">  
  5.   </div>  
  6.   <div class="form-group">  
  7.     <label for="comment">Comment</label>  
  8.     <textarea ngModel name="comment" id="comment" cols="30" rows="10" class="form-control"></textarea>  
  9.   </div>  
  10.   <button class="btn btn-primary">Submit</button>  
  11. </form>  

Adding Validation

Now let’s make this firstName field required. So in HTML5 we can use required attribute on the html elements. And in Angular we also use this attribute and we attach a div next to the input element to display the error message and we attach the bootstrap classes as well to make this div message more pretty.

  1. <div class="form-group">  
  2.     <label for="firstName">FirstName</label>  
  3.     <input ngModel name="firstName" required #firstName="ngModel" id="firstName" (change)="log(firstName)" type="text" class="form-control">  
  4.     <div class="alert alert-danger">First Name Is Required.</div>  
  5. </div>  

We don’t wanna display this all the time, we wanna display this only if the input field is not valid. So here we use ngIf directive and here you might be thinking what is the check we’ll use here. So the answer of this question is we check the valid property of FormControl and if the valid is true then it is ok otherwise there is a problem.

  1. <div class="form-group">  
  2.     <label for="firstName">FirstName</label>  
  3.     <input ngModel name="firstName" required #firstName="ngModel" id="firstName" (change)="log(firstName)" type="text" class="form-control">  
  4.     <div class="alert alert-danger" *ngIf="!firstName.valid">First Name Is Required.</div>  
  5. </div>  

So let’s test the result.

Adding Validation

So here the validation message is initially appeared on the screen which is not good and we need to fix this. But before fixing this issue, if you write something in this textbox you’ll see the validation message is disappear and if you again remove the text the validation message appears which means our validation message is working. But let’s fix the issue, we just want to show when the input field is focus out and if the text is valid then it is ok otherwise show the message.

  1. <form>  
  2.   <div class="form-group">  
  3.       <label for="firstName">FirstName</label>  
  4.       <input ngModel name="firstName" required #firstName="ngModel" id="firstName" (change)="log(firstName)" type="text" class="form-control">  
  5.       <div class="alert alert-danger" *ngIf="firstName.touched && !firstName.valid">First Name Is Required.</div>  
  6.   </div>  
  7.   <div class="form-group">  
  8.     <label for="comment">Comment</label>  
  9.     <textarea ngModel name="comment" id="comment" cols="30" rows="10" class="form-control"></textarea>  
  10.   </div>  
  11.   <button class="btn btn-primary">Submit</button>  
  12. </form>  

And now it is working properly.

Validation Error Messages

So we have seen the required validation error. In Angular we have few built-in validators that are based on HTML5 validation attribute. So let’s say we want to set the minimum and maximum length in characters of firstName.

  1. <input ngModel name="firstName" minlength="3" maxlength="10" required #firstName="ngModel" id="firstName" (change)="log(firstName)" type="text" class="form-control">  

Now let’s say we want to set the pattern for firstName then we’ll use the pattern attribute in which we’ll use the regular expression. But for demo purpose here we just write the name of any keyword in pattern attribute.

  1. <input ngModel name="firstName" pattern="apple" minlength="3" maxlength="10" required #firstName="ngModel" id="firstName" (change)="log(firstName)" type="text" class="form-control">  

So just apple is the valid value in this input field.

When we’ve multiple validation attributes, we can’t show them all the validation messages with just one div like we’re doing here.

  1. <div class="alert alert-danger" *ngIf="firstName.touched && !firstName.valid">First Name Is Required.</div>  

We place multiple divs with *ngIf directive.

  1. <div class="form-group">  
  2.     <label for="firstName">FirstName</label>  
  3.     <input ngModel name="firstName" pattern="apple" minlength="3" maxlength="10" required #firstName="ngModel" id="firstName" (change)="log(firstName)" type="text" class="form-control">  
  4.     <div class="alert alert-danger" *ngIf="firstName.touched && !firstName.valid">  
  5.       <div *ngIf="firstName.errors.required">First Name Is Required.</div>  
  6.       <div *ngIf="firstName.errors.minlength">First Name Should Be Minimum 3 Characters.</div>  
  7.       <div *ngIf="firstName.errors.pattern">First Name Doesn't Match the Pattern.</div>  
  8.     </div>  
  9. </div>  

Now, let’s inspect this in the browser. So here, we see the results,

results

This is how the multiple validation constraints work in an input field. So, we just use HTML5 standard validators and for each validation message for the validator, we’ve a separate div. Now, let’s see the content of the errors object.

content of the errors object

Now as you can see, we’ve 2 more properties in minlength. And we can also use them dynamically in our application to generate the dynamic message in the browser instead of hard coding the numbers. So,

  1. <div class="form-group">  
  2.     <label for="firstName">FirstName</label>  
  3.     <input ngModel name="firstName" pattern="apple" minlength="3" maxlength="10" required #firstName="ngModel" id="firstName" (change)="log(firstName)" type="text" class="form-control">  
  4.     <div class="alert alert-danger" *ngIf="firstName.touched && !firstName.valid">  
  5.       <div *ngIf="firstName.errors.required">First Name Is Required.</div>  
  6.       <div *ngIf="firstName.errors.minlength">First Name Should Be Minimum {{ firstName.errors.minlength.requiredLength }} Characters.</div>  
  7.       <div *ngIf="firstName.errors.pattern">First Name Doesn't Match the Pattern.</div>  
  8.     </div>  
  9. </div>  

And now if we open the browser. And yes it is working properly and showing us the validation message in the browser.

Styling Invalid Input Fields

Now you know how the error message is showing into the browser. As the best practice, we should highlight the invalid input fields. This improves the usability of our form. So let’s see how do we apply the red border to this input field? Let’s inspect the input textbox.

Styling Invalid Input Fields

Look there are so many attributes in the input field. Don’t worry about them. Just focus on the classes of the input fields and we’ll apply the css on these classes. For styling we’ve 2 options either to choose the component css file for styling or to choose the global file style.css and as we want to apply the css globally in the application so that wherever we’ll display the validation message, the input textbox should be bordered with red.

  1. @import "~bootstrap/dist/css/bootstrap.css";  
  2.   
  3. body{  
  4.     padding: 20px;  
  5. }  
  6.   
  7. .form-control.ng-touched.ng-invalid {  
  8.     border: 2px solid red;  
  9. }  

Cleaner Template

As you build forms with Angular, you’ll notice that templates get more complex. And we need to scroll horizontally a lot. And the best practice is that your code should be formatted in such a way that another developer doesn’t have to scroll to the right and to the left to see what’s happening. So the cleaner way is to write the template, break up the horizontally lengthy lines (1 attribute on each line)

  1. <form>  
  2.   <div class="form-group">  
  3.       <label for="firstName">FirstName</label>  
  4.       <input   
  5.         required   
  6.         pattern="apple"   
  7.         minlength="3"   
  8.         maxlength="10"   
  9.         ngModel   
  10.         name="firstName"   
  11.         #firstName="ngModel"   
  12.         id="firstName"   
  13.         (change)="log(firstName)"   
  14.         type="text"   
  15.         class="form-control">  
  16.       <div   
  17.         class="alert alert-danger"   
  18.         *ngIf="firstName.touched && !firstName.valid">  
  19.         <div *ngIf="firstName.errors.required">  
  20.           First Name Is Required.  
  21.         </div>  
  22.         <div *ngIf="firstName.errors.minlength">  
  23.           First Name Should Be Minimum {{ firstName.errors.minlength.requiredLength }} Characters.  
  24.         </div>  
  25.         <div *ngIf="firstName.errors.pattern">  
  26.           First Name Doesn't Match the Pattern.  
  27.         </div>  
  28.       </div>  
  29.   </div>  
  30.   <div class="form-group">  
  31.     <label for="comment">Comment</label>  
  32.     <textarea   
  33.     ngModel   
  34.     name="comment"   
  35.     id="comment"   
  36.     cols="30"   
  37.     rows="10"   
  38.     class="form-control">  
  39.   </textarea>  
  40.   </div>  
  41.   <button class="btn btn-primary">Submit</button>  
  42. </form>  

If you observe in the firstName input field, all the validation attributes come first and then ngModel with name attribute and then rest of the attributes. And here is 1 attribute on 1 line.

ngForm

So we’ve learned when we apply the ngModel directive to the input field with name attribute, Angular creates the FormControl object under the hood and associates that with this input field. We also knew that we’ve another class in Angular is FormGroup which represents one input field and FormGroup which represents a group of FormControl, now each form is a FormGroup because it has multiple controls inside.

Angular by default apply the directive ngForm on the form element in the template. Let’s go to the angular.io and let’s have a look on the documentation of ngForm directive and Selector section and you’ll get an idea if you use form element in the template then ngForm automatically apply to it. We can create the template variable to get the reference to that ngForm directive. So,

  1. <form #f="ngForm">  
  2. </form>  

Now, if we come again to the documentation, we’ll see the output property of ngForm is ngSubmit.

We already know that the output property is used to raise the custom events so ngSubmit exposes the output property that we can use in our event binding expressions.

  1. <form #f="ngForm" (ngSubmit)="submit(f)">  
  2. </form>  

You can see we’re passing as an argument to the submit method ‘f’ which is template variable contains the ngForm directive object.

Now, let’s see the ngForm directive in the console. For this purpose, let’s define the submit() method in the component.

  1. export class ContactFormComponent {  
  2.   log(x) {  
  3.     console.log(x);  
  4.   }  
  5.   
  6.   submit(f) {  
  7.     console.log(f);  
  8.   }  
  9. }  

Now, fill the form in the browser and submit the form and open the console. And here you’ll see the NgForm object containing some familiar properties. So all those pair properties you saw earlier, we’ve here as well,

properties

And if you can see here we’ve form property which is the FormGroup object and FormGroup represents the group of input fields and if you expand form property, you’ll again see the pair properties. So when the ngForm directive is apply to a form element basically it creates the object and it exposes the pair properties. And we can use these pair properties in component action as well.

  1. submit(f) {  
  2.   // whatever  
  3.   f.valid....  
  4.   f.invalid  
  5. }  

Now one important thing is here we’ve an important property ‘value’ as well to track the request header.

property ‘value’

So those name attribute we assigned to each input field determine the name of the key or the property in this value object. So this value object is a json representation of our form. And in the real world scenario, we’ve submit handler here can simply get f.value and then we’ve a json object that we can send to an API on the server persistence.

  1. submit(f) {  
  2.   console.log(f.value);  
  3. }  

ngModelGroup

Sometimes when you’re working with complex forms, you might have multiple groups in your form. So back in our previous example, let’s make the structural groups in the form. Just like we have an ngModel directive, we also have an ngModelGroup directive. So back in our template and add the div with the ngModelGroup directive and set the value with “contact” and inside this div, place the firstName html elements like label and input textbox.

  1. <div ngModelGroup="contact">  
  2.     <div class="form-group">  
  3.         <label for="firstName">FirstName</label>  
  4.         <input   
  5.           required   
  6.           pattern="apple"   
  7.           minlength="3"   
  8.           maxlength="10"   
  9.           ngModel   
  10.           name="firstName"   
  11.           #firstName="ngModel"   
  12.           id="firstName"   
  13.           (change)="log(firstName)"   
  14.           type="text"   
  15.           class="form-control">  
  16.         <div   
  17.           class="alert alert-danger"   
  18.           *ngIf="firstName.touched && !firstName.valid">  
  19.           <div *ngIf="firstName.errors.required">  
  20.             First Name Is Required.  
  21.           </div>  
  22.           <div *ngIf="firstName.errors.minlength">  
  23.             First Name Should Be Minimum {{ firstName.errors.minlength.requiredLength }} Characters.  
  24.           </div>  
  25.           <div *ngIf="firstName.errors.pattern">  
  26.             First Name Doesn't Match the Pattern.  
  27.           </div>  
  28.         </div>  
  29.     </div>  
  30. </div>  

One more thing! If you want to see complete ngForm object then change the code of submit event handler as,

  1. submit(f) {  
  2.   console.log(f);  
  3. }  

And if you want to see just the values here.

  1. submit(f) {  
  2.   console.log(f.value);  
  3. }  

But now, here I want a complete object, so I’m going with the first approach. Save the file and submit the form.

file and submit the form

This is the beauty of ngModelGroup. Now our value object is the complex object which is a hierarchical value containing contact and then contact contains firstName. So in your application, the API you’ve on the server makes the complex nested object structure like this. That’s the case you use ngModelGroup directive. Also similar to the ngModel, you can get the reference of the ngModelGroup directive by using template variable and here we can define the template variable.

  1. <div ngModelGroup="contact" #contact="ngModelGroup">  
  2. </div>  

Now we can use this contact variable anywhere in this template and this is useful if you wanna validate an entire group as a whole. Let’s say you’ve a group called billingdetails and you wanna display all the validation errors for billingdetails on the top of that group, if that’s the case we’ll add one more div here.

  1. <div ngModelGroup="contact" #contact="ngModelGroup">  
  2.   <div *ngIf="!contact.valid">...</div>  
  3. </div>  

Now here is our final template code.

  1. <form #f="ngForm" (ngSubmit)="submit(f)">  
  2.   <div ngModelGroup="contact" #contact="ngModelGroup">  
  3.     <div *ngIf="!contact.valid">...</div>  
  4.     <div class="form-group">  
  5.         <label for="firstName">FirstName</label>  
  6.         <input   
  7.           required   
  8.           pattern="apple"   
  9.           minlength="3"   
  10.           maxlength="10"   
  11.           ngModel   
  12.           name="firstName"   
  13.           #firstName="ngModel"   
  14.           id="firstName"   
  15.           (change)="log(firstName)"   
  16.           type="text"   
  17.           class="form-control">  
  18.         <div   
  19.           class="alert alert-danger"   
  20.           *ngIf="firstName.touched && !firstName.valid">  
  21.           <div *ngIf="firstName.errors.required">  
  22.             First Name Is Required.  
  23.           </div>  
  24.           <div *ngIf="firstName.errors.minlength">  
  25.             First Name Should Be Minimum {{ firstName.errors.minlength.requiredLength }} Characters.  
  26.           </div>  
  27.           <div *ngIf="firstName.errors.pattern">  
  28.             First Name Doesn't Match the Pattern.  
  29.           </div>  
  30.         </div>  
  31.     </div>  
  32.   </div>  
  33.   <div class="form-group">  
  34.     <label for="comment">Comment</label>  
  35.     <textarea   
  36.     ngModel   
  37.     name="comment"   
  38.     id="comment"   
  39.     cols="30"   
  40.     rows="10"   
  41.     class="form-control">  
  42.   </textarea>  
  43.   </div>  
  44.   <button class="btn btn-primary">Submit</button>  
  45. </form>  

Control Classes And Directive

Now if you’re confused about all this classes and directives let me make it super simple thing. In Angular, we’ve 2 classes to keep track of the state of input fields and their validity

  • FormControl (which represents only 1 input field)
  • FormGroup (which represents the group of input field)

Now when we apply the ngModel directive on an input field, Angular automatically creates the FormControl object and associates that with that input field. Input field must contain name attribute as well. And FormGroup class is used to represent entire form and optionally groups within a form. As we explain before, we’ve directive called ngForm that is automatically applied to all form elements but it is the good approach to apply the ngForm explicitly on the form element. ngForm directive internally creates the FormGroup directive and associated with the form. And with this FormGroup object, we can track the state changes of the form and its validity. Now if we have a complex form with multiple subgroups, we can optionally apply ngModel directive in the subgroup and this directive similar to the ngForm directive will also create a FormGroup object.

Control Classes And Directive

Now you might be curious what is the difference between ngForm and ngModelGroup. The difference is ngForm directive exposes an output property ngSubmit which is used to handle the submit event of forms, ngModelGroup doesn’t have that output property. It doesn’t make sense to submit the part of the form. So this was all about the FormControl, FormGroup and their related directives.

Disabling the Submit Button

There is a tiny problem in this form. I can simply click the submit button without providing the valid value for the firstName. So ideally in this case, we need to disable this button and enable it only if the form is in the valid state. Let’s see how to do this,

So earlier we declare the template variable ‘f’ and set it to the ngForm directive. Using this directive, we can check the validity of the form as a whole. So here we’ll use the property binding disabled and set it to the ngForm template variable is not valid.

  1. <button class="btn btn-primary" [disabled]="!f.valid">Submit</button>  

Now save the file and open the form in the browser and observe the result.

Disabling the Submit Button

Working With Checkboxes

We’ve only looked at input fields of type text. Let’s see how to add the checkbox in the form.

Zen Coding

  1. div.checkbox>label>input[type=’checkbox’] Press TAB  

And it generates the code,

  1. <div class="checkbox">  
  2.   <label for="">  
  3.     <input type="checkbox">  
  4.   </label>  
  5. </div>  

So here we don’t need the for attribute of label control. Now add the label next to checkbox.

  1. <div class="checkbox">  
  2.   <label>  
  3.     <input type="checkbox"> Subscribe to Mailing List  
  4.   </label>  
  5. </div>  

Now similar to other input fields, we need to apply ngModel here as well. So,

  1. <div class="checkbox">  
  2.   <label>  
  3.     <input  
  4.        type="checkbox"  
  5.        ngModel  
  6.        name="isSubscribed"  
  7.     >Subscribe to Mailing List</label>  
  8. </div>  

Now here is a trick, we want to see the FormControl object elements in the browser. And this object is of type json. Now we’ll also format it with json pipe to view the things into the browser.

  1. <div class="checkbox">  
  2.   <label>  
  3.     <input  
  4.        type="checkbox"  
  5.        ngModel  
  6.        name="isSubscribed"  
  7.     >Subscribe to Mailing List</label>  
  8. </div>  
  9. <p>  
  10.   {{ f.value | json }}  
  11. </p>  

Now open the browser,

browser

Here we’ve 3 key value pairs in json in browser. And currently none of these keys have value. But if we place some text in input fields and change the value of the checkbox then the value becomes change in the json object at the same moment.

json object

So we have seen adding the checkboxes is exactly like adding the textbox.

Working With Drop-down List

Now let’s see how to add drop down list to the form. So back in our template, just before the json object. We’ll add the div,

Zen Coding

  1. div.form-group>label[for= ’contactMethod’]+select[id=’contactMethod’].form-control Press Tab  

Results

  1. <div class="form-group">  
  2.   <label for="contactMethod"></label>  
  3.   <select name="" id="contactMethod" class="form-control"></select>  
  4. </div>  

So here is the markup. Just like other input fields we need to apply ngModel on select element as well.

  1. <div class="form-group">  
  2.   <label for="contactMethod">Contact Method</label>  
  3.   <select ngModel  
  4.       name="contactMethod"  
  5.       id="contactMethod"  
  6.       class="form-control">  
  7.   </select>  
  8. </div>  

Now we need to populate this dropdown list. So as you know in HTML, we’ve one or more options inside the select element. But in the real world application most of the time we don’t hard code the options in select html element. We actually call the API on the server to get, let’s say the list of contactMethod and then we populate this drop down list dynamically. So let’s see how we do this.

We go back to component and declare the array of contactMethod.

  1. export class ContactFormComponent {  
  2.   contactMethod = [  
  3.     { id: 1, name: 'Email' },  
  4.     { id: 2, name: 'Phone' },  
  5.     { id: 3, name: 'Mobile' }  
  6.   ];  
  7.   
  8.   log(x) {  
  9.     console.log(x);  
  10.   }  
  11.   
  12.   submit(f) {  
  13.     console.log(f);  
  14.   }  
  15. }  

Now back in ContactForm template and here we’ll define the options with ngFor.

  1. <option *ngFor="let method of contactMethods" value=""></option>  

And now we bind this value property with property binding syntax with method.id and for dropdown select text we use interpolation.

  1. <option *ngFor="let method of contactMethods" [value]="method.id">  
  2.   {{ method.name }}  
  3. </option>  

Now let’s look at the result. So back in the browser,

Result

Now we want to add one more option like Choose Contact Method which is the convention in most of the applications.

  1. <select ngModel  
  2.     name="contactMethod"  
  3.     id="contactMethod"  
  4.     class="form-control">  
  5.     <option value="" selected>Choose Contact Method</option>  
  6.     <option *ngFor="let method of contactMethods" [value]="method.id">  
  7.       {{ method.name }}  
  8.     </option>  
  9. </select>  

And now it is working as expected.

Types of Forms

Now let’s take it to the next level, in most of the application we just send the value of the option to the server like 1, 2, 3 etc. But sometimes we want to send the actual contact object or I would say the complex object containing id and name. Let’s see how can we pass the complex object. So inspect the dropdown list,

dropdown list

If you see here in html, value is 1 in string and we can’t store actual a contact object here. To solve this issue, we need to go back to our template and instead of binding the value property, we need to bind ngValue. So ngValue is the attribute directive that exposes ngValue property and we can bind this to a complex object like the method or contact method object

  1. <select ngModel  
  2.     name="contactMethod"  
  3.     id="contactMethod"  
  4.     class="form-control">  
  5.     <option value="" selected>Choose Contact Method</option>  
  6.     <option *ngFor="let method of contactMethods" [ngValue]="method">  
  7.       {{ method.name }}  
  8.     </option>  
  9. </select>  

Look now we’re passing the complex object to the ngValue directive. Now let’s test it in the browser.

ngValue directive

Now we can see the value of the contactMethod property is an actual object with 2 properties (id and name). Now you’ve an idea how to pass the complex object. Now revert it back.

Now one more thing, we wanna allow this to select the multiple options to the user. So here we use multiple html attribute with select element.

multiple html attribute with select element

We can see how the dropdown with multiple attribute looks and how the items value are selected.

Working With Radio Buttons

Now let’s see how we use radio buttons. So to add the radio button using bootstrap, we need the markup like this,

Zen Coding

  1. div.radio>label>input[type=’radio’][name=’contactRadio’] Press TAB  

Results

  1. <div class="radio">  
  2.   <label>  
  3.     <input type="radio" name="contactRadio">  
  4.   </label>  
  5. </div>  

Radio button is used to select only 1 option. And we need to instruct with the group of radio buttons in which we can select only 1 radio button option. And to group the radio button in a group, we use name attribute with input radio. Now let’s mention the label and set the value of the radio button.

  1. <div class="radio">  
  2.   <label>  
  3.     <input type="radio" name="contactRadio" value="1"> Email  
  4.   </label>  
  5. </div>  

Now copy the same code and paste it again with Phone and value 2.

  1. <div class="radio">  
  2.     <label>  
  3.       <input type="radio" name="contactRadio" value="2"> Phone  
  4.     </label>  
  5. </div>  

Now just like other input fields we apply ngModel here as well on each of these radio buttons.

  1. <div class="radio">  
  2.   <label>  
  3.     <input ngModel type="radio" name="contactRadio" value="1"> Email  
  4.   </label>  
  5. </div>  
  6. <div class="radio">  
  7.     <label>  
  8.       <input ngModel type="radio" name="contactRadio" value="2"> Phone  
  9.     </label>  
  10. </div>  

Now let’s take a look into browser,

Working With Radio Buttons

So this was the working with hard coding of value fields in radio controls. Now let’s make it dynamic.

Delete the 2nd radio button.

  1. <div *ngFor="let method of contactMethods" class="radio">  
  2.   <label>  
  3.     <input ngModel type="radio" name="contactRadio" [value]="method.id"> {{ method.name }}   
  4.   </label>  
  5. </div>  

This is how we make the radio button dynamic. Now look it is rendered dynamically in the browser.

radio button dynamic

What We Have Learned

So let’s summarize what we’ve learned in this article. So here we’ll build a new form from beginning to end.

Types of Forms

And here is the form. So everything I’ve already discussed. So here we’ll just discuss the necessary things.

First of all make a separate component and design this form for this component. And here is the component code,

  1. import { Component } from '@angular/core';  
  2.   
  3. @Component({  
  4.   selector: 'app-form',  
  5.   templateUrl: './form.component.html',  
  6.   styleUrls: ['./form.component.css']  
  7. })  
  8. export class FormComponent {  
  9.   Courses = [  
  10.     { id: 1, name: 'Development' },  
  11.     { id: 2, name: 'Art' },  
  12.     { id: 3, name: 'Language' }  
  13.   ];  
  14.   
  15.   log(x) {  
  16.     console.log(x);  
  17.   }  
  18.   
  19.   submit(course) {  
  20.     console.log(course);  
  21.   }  
  22. }  

Now form.component.html code is,

  1. <form #f="ngForm" (ngSubmit)="submit(f.value)">  
  2.   <div class="form-group">  
  3.     <label for="name">Course name</label>  
  4.     <input   
  5.       required  
  6.       minlength="5"   
  7.       ngModel  
  8.       name="name"  
  9.       #name="ngModel"  
  10.       type="text"   
  11.       (change)="log(name)"  
  12.       id="name"   
  13.       class="form-control">  
  14.       <div *ngIf="name.touched && name.invalid" class="alert alert-danger">  
  15.         <div *ngIf="name.errors.required">  
  16.           Name is Required  
  17.         </div>  
  18.         <div *ngIf="name.errors.minlength">  
  19.           Name should be atleast {{ name.errors.minlength.requiredLength }} characters  
  20.         </div>  
  21.       </div>  
  22.   </div>  
  23.   <div class="form-group">  
  24.     <label for="category">Select Category</label>  
  25.     <select required ngModel name="category" #category="ngModel" id="category" class="form-control">  
  26.       <option value="">Choose Category</option>  
  27.       <option *ngFor="let course of Courses" [value]="course.id">  
  28.         {{ course.name }}  
  29.       </option>  
  30.     </select>  
  31.     <div *ngIf="category.touched && category.invalid" class="alert alert-danger">  
  32.         <div *ngIf="category.errors.required">  
  33.           Category Is Required  
  34.         </div>  
  35.       </div>  
  36.   </div>  
  37.   <div class="checkbox">  
  38.     <label>  
  39.       <input ngModel name='isGuaranteed' type="checkbox"> 30-day money back guarantee  
  40.     </label>  
  41.   </div>  
  42.   <p>  
  43.     {{ f.value | json }}  
  44.   </p>  
  45.   <button class="btn btn-primary" [disabled]="!f.valid">Create</button>  
  46. </form>  

We create the template variables for validation messages, that’s why we create #category=”ngModel” in input html elements. One more thing is if you see the (ngSubmit) output attribute of the form, we’re passing the value property of the ngForm object to the submit function which is containing all the form values. And we’re displaying these values on the console.

values on the console

This is how here in Angular, we track the values of the form which goes with the submit request.


Similar Articles