Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Working With Angular Template Forms

DZone's Guide to

Working With Angular Template Forms

Working with Angular forms tends to be one of the more complicated things in Angular. Luckily, there is an easy way to start with Angular and template forms.

· Web Dev Zone
Free Resource

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

In this blog, I want to show you how to work with Angular’s template-driven forms.

The Problem

I was recently giving an Angular course and I noticed that working with Angular forms seems to be one of the most complicated things in Angular.

The Situation

We will start with a simple Angular application with a structure like this:

app
├── app.component.ts
├── app.component.html
├── form.component.ts
├── form.component.html
├── app.module.ts
└── main.ts
index.html
systemjs.config.js
tsconfig.json
tslint.json
styles.css
package.json

This is an easy way to start with Angular and template forms.

(I did not mention files like Favicon. They do not really impact our work here.)

Now, let's go create a simple form. For this, I will grab Bootstrap's most simple form just to get a nice look and feel.

Now, we add the bootstrap CDN to our index.html.

<head>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>

Out app.component.html simply looks like this:

<div class="container">
    <h1>{{title}}</h1>
    <form-component></form-component>
</div>

Introducing the Form

We remove the checkbox and the file upload from the copied form from Bootstrap just for clarity and add the form template to our form component. Further, we want to handle a simple Name for understanding. Let's modify the template to only work with a name.

So, our form.component.html looks like this now:

<form>
    <div class="form-group">
        <label for="exampleInputName">Name</label>
        <input type="name" class="form-control" id="exampleInputName" placeholder="Name">
    </div>
    <button type="submit" class="btn btn-default">Submit</button>
</form>

So, what we've done here so far is nothing Angular related! We just added simple HTML form things.

We will now go ahead and make this thing an Angular form.

Add Angular to the Form

The Model

We will use a normal customer to deal with the form. Also nothing spectacular here; we just add a class representing this customer:

export class Customer {
    constructor(public name: string) { }
}

We place it in a special folder called models

app
├── models
    └──  customer.model.ts
├── app.component.ts
├── app.component.html
├── form.component.ts
├── form.component.html
├── app.module.ts
└── main.ts
index.html
systemjs.config.js
tsconfig.json
tslint.json
styles.css
package.json

Then, we can make a new model in the component constructor like this:

export class FormComponent {
  public title: string = 'Template Forms';
  public model: Customer;

  constructor() {
    this.model = new Customer('Fabian');
  }
}

Add Binding to the Model With ngModel

The next thing is to add the binding to the inputs in our forms. Here is the first time we marry Angular with our HTML.

We take the ngModel-directive to tell the field “Hey, this thing I give you here is important for your form. Please, my little form, take care of it.”

<div class="container">
    <!--...-->
     <input type="name" class="form-control" id="exampleInputName" placeholder="Name" ngModel>
    <!--...-->
</div>

Now, this input is marked with the ngModel-directive. However, what is that good for?

Before we go on, clarify that it can also take care of something Angular prepared for us. What we want to do now is ask our form about its state. What is the state of our form currently and when the user types something in? We cannot access our form yet because we do not have a name to ask for. So, let’s introduce one!

We can do this by adding a variable in the template with the # sign and tagging the form with the ngForm-directive Angular prepared for us.

This means:

<form>

...becomes:

<form #myform="ngForm">

We have introduced a variable and assign the complete form to it. Angular introduced this ngForm for us in the background, we are only catching it up and take a variable to make it accessible. Now we are able to ask the form for its state! This variable is called a template reference variable.

What we want is to see the state of our form. We achieve that by adding the ngModel-directive by giving the form a variable we can access it and one last piece is missing. If we use ngModel in form control, we also have to tell the Angular form how it should be accessed. So, we need a name. Let's introduce a name that this control can be registered onto the form under that name.

<div class="container">
    <!--...-->
     <input type="text" class="form-control" id="exampleInputName" placeholder="Name" ngModel name="name">
    <!--...-->
</div>

With those three things we can ask the form about its state with myform.value like this:

<p> {{ myform.value | json }} </p>

<form #myform="ngForm">
    <div class="form-group">
        <label for="exampleInputName">Name</label>
        <input type="text" class="form-control" id="exampleInputName" placeholder="Name" ngModel name="name">
    </div>
    <button type="submit" class="btn btn-default">Submit</button>
</form>

The ngModel directive is forcing Angular to persist the state of the form to the form object. So, we read it with:

{ myform.value | json }

However, other to that, it is doing nothing. We got our state only persisted to the form. We want to interact with our model, right?

Therefore, we have to introduce some kind of data binding. As we know, data binding is working with () and/or [] and we have no data binding going on here so far.

We could do the property binding like this:

 <input ... [ngModel]="model.name" name="name">

Then Angular would take this ngModel directive and connect it to a property on our component called model.name.

Remember that we created a model on our component with its constructor.

When the initial value is set from the component, the value would be reflected into the input field. However, this is only one-way binding! When the value in the input changes, it will not be reflected in the model. The form will change its state but the model knows nothing about it.

To get the model also changed when the user types something in we have to introduce two-way binding like this:

 <input ... [(ngModel)]="model.name" name="name">

So, the forms state would now reflect the changes into the forms state and into the model we want to work with when submitting. In addition to that the initial state is set because we create a model on the constructor.

Further Steps With the Form

We can use the forms template reference variable also to check some properties on the form.

The template reference variable offers us a form-property which we can check

We can check the following states on the form:

<!-- if user touched every field of the form -->
<p>{{myform.form.touched}}</p>

<!-- if every field of the form is not touched yet -->
<p>{{myform.form.untouched}}</p>

<!-- field has not been changed value since it is displayed -->
<p>{{myform.form.pristine}}</p>

<!-- user changed the value since it is displayed -->
<p>{{myform.form.dirty}}</p>

<!-- form is valid -->
<p>{{myform.form.valid}}</p>

<!-- form is invalid -->
<p>{{myform.form.invalid}}</p>

Let's face the “valid” and “invalid” for a second. We can apply a normal HTML required attribute at one control just to have a reason why a form should be valid or invalid.

<form #myform="ngForm">
    <div class="form-group">
        <label for="exampleInputName">Name</label>
        <input type="text" class="form-control" id="exampleInputName" placeholder="Name" [(ngModel)]="model.name" name="name" required>
    </div>
    <button type="submit" class="btn btn-default">Submit</button>
</form>

So, we can go ahead and, for example, disable the submit button and show a message when the form is not valid.

<form #myform="ngForm">
    <div class="form-group">
        <label for="exampleInputName">Name</label>
        <input type="text" class="form-control" id="exampleInputName" placeholder="Name" [(ngModel)]="model.name" name="name" required>
    </div>

    <span *ngIf="!myform.form.valid">Check your form</span>

    <button type="submit" class="btn btn-default" [disabled]="!myform.form.valid">Submit</button>

</form>

So, this gives us the possibility to ask the form for some variables Angular gives us.

We can also do this whole template variable thing on a control itself and ask the simple control for its state instead of the whole form. The same properties that are valid for the form apply also to the control. Therefore, we have to grab our ngModel-directive again and assign it to a template variable to make the control, or better, make the model assigned to this control accessible.

 <input type="text" class="form-control" id="exampleInputName" placeholder="Name" [(ngModel)]="model.name" name="name" required #name="ngModel">

Now we can ask for the state of this control in the same way like the form:

<form #myform="ngForm"> <div class="form-group"> <label for="exampleInputName">Name</label> <input type="text" class="form-control" id="exampleInputName" placeholder="Name" [(ngModel)]="model.name" name="name" required #name="ngModel"> </div> <span *ngIf="!name.valid">name is invalid</span> <span *ngIf="!myform.form.valid">Check your form</span> <button type="submit" class="btn btn-default" [disabled]="!myform.form.valid">Submit</button> </form>

Submitting the Form

Our button at the end of the form is currently set to submit the form. However, we have not caught the submitting so far. So, let's do that.

<h4>Form Value</h4>

<p>{{myform.value | json}}</p>

<form #myform="ngForm" (ngSubmit)="alertMyFormModel()">
    <div class="form-group">
        <label for="exampleInputEmail1">Email address</label>
        <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email" ngModel name="email">
    </div>
    <div class="form-group">
        <label for="exampleInputPassword1">Password</label>
        <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
    </div>
    <button type="submit" class="btn btn-default">Submit</button>
</form>

We introduced the ngSubmit-directive and assigned it a function we have to implement on our component.

export class FormComponent {
  public title: string = 'Template Forms';
  public model: Customer;

  constructor() {
    this.model = new Customer('Fabian');
  }

  public alertMyFormModel() {
    alert(JSON.stringify(this.model));
  }
}

Now, if you click the button, the form gets submitted and you should see the model in a JSON string in an alert box.

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:
tutorial ,web dev ,angular ,template forms ,bootstrap

Published at DZone with permission of Fabian Gosebrink, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}