Template Driven Angular Forms

by | Jul 25, 2018

In this blog post we will explore Angular forms built using it’s template syntax. Angular provides developers two ways to build forms, one uses it’s template syntax and the other is a model-driven approach. Forms built using the model driven approach are called “Reactive Forms” and I will follow this post up with a post on Reactive Forms.

To build a template driven Angular form you should be familiar with Angular’s template syntax. Using the template syntax and a few form-specific directives will allow developers to build robust forms using primarily html code.

Before we jump in, here are some links to other Angular Tutorials I’ve put together:

 

Ng Example

A quick note, this post is a part of a series of Angular posts. All of code in this post, as well as links to the other posts in this series can be found on GitHub. In this example I will be building a simple user admin form for creating a new user. Bootstrap 4 has been added to the project and will be used in building the Angular form.

 

Module Setup

For this example we are going to create a FormsExampleModule that will hold all of our necessary content for the example. Generating this module and the following components. I am going to add some setup for the reactive example here as well but feel free to skip the reactive component if you are not interested. The “core” portion of the following commands reflects how this example repo has been setup, if you aren’t following along from previous examples or your project is setup differently, then “core” can be omitted.

  1. ng g module core/forms-example
  2. ng g component core/forms-example
  3. ng g component core/forms-example/template-example
  4. ng g component core/forms-example/reactive-example

Next I updated the routing inside the example application as I have previously for other examples. I won’t demonstrate those steps again here.

Our generated FormsExampleModule is a bit like a stand alone application at this point. We are going to have to provide the module with the correct application pieces to build our template driven Angular form. What I mean by that; we are going to be using form specific directives to build our Angular form, thus we need to bring those directives into our module to use them. We do this by importing the “FormsModule” and providing it to the “imports” array of our FormsExampleModule. After that we can get started building our template driven Angular form.

 

User Class

Next we are going to create our “User” class. This will act as our object model that will hold the information for our user. Create a user.ts file somewhere in your project and then create the following user class.

export class User {
    firstName: string;
    lastName: string;
    role: string;
    notes?: string;
}

Pretty standard stuff for a simple user class that we can use for our data binding. Next let’s build out the form.

 

Building Our Template

Now we are going to use standard html to build out the Angular form template. I have placed the template example component inside of a bootstrap card in theforms-example.component.html.

  <div class="w-50 p-2">
    <div class="card">
      <div class="card-header text-center">
       <h5>Template Form Example</h5>
      </div>
      <div class="card-body">
        <app-template-example></app-template-example>
      </div>
    </div>
  </div>

Add the following code block to the template-example.component.html file. The html for the form itself looks like this:

<form>
  <div class="form-group">
    <label for="firstName">First Name</label>
    <input class="form-control" name="firstName" id="firstName" type="text">
  </div>
  <div class="form-group">
    <label for="lastName">Last Name</label>
    <input class="form-control" name="lastName" id="lastName" type="text">
  </div>
  <div class="form-group">
    <label for="role">User Role</label>
    <select class="form-control" name="role" id="role">
      <option>Guest</option>
      <option>Admin</option>
      <option>Owner</option>
      <option>Operator</option>
    </select>
  </div>
  <div class="form-group">
    <label for="notes">Additional Notes</label>
    <textarea class="form-control" name="notes" id="notes" rows="6" placeholder="Add additional notes here."></textarea>
  </div>
</form>

This is all standard html with some additional bootstrap classes for formatting. Now we have a form built, but it can’t really do anything at the moment. Now we have to use our form directives that we imported with our FormsModule earlier.

 

Initializing Our User

Next in the template-example.component.ts we need to initialize our user that we are going to be binding to the form. Import the user interface we created previously and initialize how you see fit. I am going to have it hold some mock data for demonstration purposes.

  user: User = {
    firstName: 'New',
    lastName: 'User',
    role: 'Guest',
    notes: undefined
  };

ngForm and ngModel

ngForm and ngModel are the Angular form directives that we are going to add to our current form to provide Angular with the right information for the framework to internally build it’s version of the form. Internally, Angular is going to create a “FormGroup” object. Somewhat interesting, a FormGroup is what you would build yourself if you were building a Reactive Form. So deep down, Angular treats Template Driven Forms and Reactive Driven Forms in the same way. The difference being how you build them and the functionality that comes with each direction. I will demonstrate how to view your Template Driven Form Group in a moment. First, let’s use the ngForm directive on our form so Angular will know to make the FormGroup out of the form.

<form #userForm="ngForm">
  <!--form content-->
</form>

Once we do this, we can actually use @ViewChild('userForm')to grab the instance of the Angular form in our component file. Now there isn’t any real practical reason to do this in a real development environment but by logging this element we can get a good look at what Angular creates in terms of the FormGroup, which I found to be some valuable insight.

Adding this code snippet to the template-example.component.ts

  @ViewChild('userForm') userForm: ElementRef;
  logForm(){
    console.log(this.userForm);
  }

And this to template-example.component.html

<div class="d-flex w-100">
  <button class="btn pull-right btn-primary" (click)="logForm()">Log Form</button>
</div>

This will allow us to view the state of our form through Angular’s eyes at any time. Now as our form is setup, if we run the application and log the form, we will get this.

 

 

 

 

 

 

 

This is the state of the form before it is touched at all (you can see this because pristine = true. As you can see there is some interesting information here. Remember we really haven’t done anything but build an html form and add the ngForm directive. Now I highlighted the controls object to show that it is currently empty. As we add our data binding we will see this object fill out. Let’s bind our first name input to our user and see what the form looks like after that.

To add a template driven control to a form you need two things:

  1. a unique name for the control (we already have added a name to each of our controls above)
  2. [(ngModel)] is what is used to bind the pieces of our user object to the form control

Additionally, I recommend adding an ngModel directive. It’s going to be useful for error messaging later, it basically provides a way to access the form control in other places within the html. After adding all of that, our firstName input field is going to look like this:

<input class="form-control" name="firstName" id="firstName" [(ngModel)]="user.firstName" #firstName="ngModel" type="text">

Again,
[(ngModel)]="user.firstName" binds the data model to the input field.
name="firstName"provides a unique name for Angular to use to build the FormGroup
#firstName="ngModel" adds the ngModel directive so we can use “firstName” to access the control object for “firstName” later on.

Now when we run the program and log our form, we will see the first name form control object in the controls object of our FormGroup.

Validation

Next I just want to provide a simple example of validation for your input fields. In my experience, it’s more difficult to do validation for a template driven form then a reactive form. The same validation is possible for both, I just find the built in validators for reactive forms to be easier to use. However, we can require fields, give them a minimum length and add forbidden values with template driven Angular forms. If that is all the validation you are really concerned about then a template driven form is definitely a viable option. Below is an example of required validation on the first name input field:

 <input class="form-control" name="firstName" id="firstName" [(ngModel)]="user.firstName" #firstName="ngModel" type="text" required>
   <div *ngIf="!firstName.valid && !firstName.pristine" class="alert alert-danger">
      <span *ngIf="firstName.errors.required">
        First Name is required
      </span>
   </div>

What I have done is added “required” to the input field, which Angular picks up on and adds to the form control object. Next I am checking the firstName form control objects properties of “valid” and “pristine” to see if there are any errors to display. If there are errors, then I check the firstName form control “errors” object to see if it is the “required” field that is missing. If there was additional validation (most native HTML form validation will work), then there might be additional errors in the error object that we would want to check for different messaging.

 

Bonus: Styling

Along with Angular creating a FormGroup object for you, it also applies classes to the input and form elements based on valid/invalid properties. “.ng-valid” and “.ng-invalid” classes will be applied as valid/invalid states change. To demonstrate this you can add the following styling class definitions to template-example.component.css

.ng-valid[required], .ng-valid.required  {
    border-left: 5px solid #42A948; /* green */
  }
  .ng-invalid:not(form)  {
    border-left: 5px solid #a94442; /* red */
  }

This will add a border on the left of input fields that are required based on their state. Once our form is completed with validation, data binding, and styling it will look something like this.

 

Conclusion

Above I have demonstrated how to build a template driven Angular form with validation and data binding. For a working example of this, check out my example repo under “Forms Example”. I recommend digging through the “FormGroup” object that is created by Angular, there may be some useful pieces of information you want to use in there. Thanks for reading, and keep an eye out for my example next.