Easy Tutorial
❮ Angularjs2 Tutorial Home ❯

Angular 2 Forms

In this chapter, we will introduce how to build an Angular form using components and templates.

Using Angular templates, we can create various types of forms, such as login forms, contact forms, product detail forms, etc., and we also add data validation to these form fields.

Next, we will implement the form functionality step by step.


Creating a Project

Import the initialized project.

For a complete project creation, refer to: Angular 2 TypeScript Environment Configuration

Or directly download the source code: Click to Download

After extracting, rename the directory to angular-forms and change "name": "angular-quickstart" to "name": "angular-forms" in the angular-forms/package.json file.

Once done, we execute cnpm install to load the dependencies.

Creating the Site Model

The following creates a simple model class Site, which includes three required fields: id, name, url, and an optional field: alexa.

Create the site.ts file under the angular-forms/app directory with the following code:

app/site.ts File:

export class Site {
  constructor(
    public id: number,
    public name: string,
    public url: string,
    public alexa?: number
  ) {  }
}

In the code below, fields marked as public are public fields, and alexa followed by a question mark (?) indicates an optional field.

Creating a Form Component

Each Angular form consists of two parts: an HTML-based template and a code-based component that handles data and user interactions.

Create the site-form.component.ts file under the angular-forms/app directory with the following code:

app/site-form.component.ts File:

import { Component } from '@angular/core';
import { Site }    from './site';

@Component({
  moduleId: module.id,
  selector: 'site-form',
  templateUrl: 'site-form.component.html'
})
export class SiteFormComponent {
  urls = ['www.tutorialpro.org', 'www.google.com',
            'www.taobao.com', 'www.facebook.com'];
  model = new Site(1, 'tutorialpro.org', this.urls[0], 10000);
  submitted = false;
  onSubmit() { this.submitted = true; }
  // TODO: Remove after completion
  get diagnostic() { return JSON.stringify(this.model); }
}

The example imports the Component decorator and the Site model.

The @Component selector "site-form" means we can include this form in a parent template using a <site-form> tag.

The templateUrl property points to a separate HTML template file named site-form.component.html.

The diagnostic property is used to return the JSON representation of the model.

Defining the Root Module of the Application

Modify app.module.ts to define the root module of the application, specifying the external references and components declared within this module, such as SiteFormComponent.

Since template-driven forms have their own module, we need to add FormsModule to the imports array of this application to use forms.

The app/app.module.ts file code is as follows:

app/app.module.ts File:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule }   from '@angular/forms';
import { AppComponent }  from './app.component';
import { SiteFormComponent } from './site-form.component';

@NgModule({
  imports: [ BrowserModule, FormsModule ],
  declarations: [ AppComponent, SiteFormComponent ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { SiteFormComponent } from './site-form.component';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule
  ],
  declarations: [
    AppComponent,
    SiteFormComponent
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

Create the Root Component

Modify the root component file app.component.ts to include SiteFormComponent.

app/app.component.ts File:

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: '<site-form></site-form>'
})
export class AppComponent { }

Create an Initial HTML Form Template

Create the template file site-form.component.html with the following code:

app/site-form.component.html File:

<div class="container">
    <h1>Website Form</h1>
    <form>
      <div class="form-group">
        <label for="name">Website Name</label>
        &lt;input type="text" class="form-control" id="name" required>
      </div>
      <div class="form-group">
        <label for="alexa">Alexa Rank</label>
        <input type="text" class="form-control" id="alexa">
      </div>
      <button type="submit" class="btn btn-default">Submit</button>
    </form>
</div>

The required attribute makes this field mandatory; if not set, it is optional.

Enter the following command in the angular-forms directory:

cnpm install bootstrap --save

Open the index.html file and add the following style link to the <head>:

<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">

After running npm start, access http://localhost:3000/, and the output will look like this:


Using ngModel for Two-Way Data Binding

Next, we will use ngModel for two-way data binding, which updates the component's properties by listening to DOM events.

Modify app/site-form.component.html to use ngModel for binding our form to the model. The code is as follows:

app/site-form.component.html File:

<div class="container">
    <h1>Website Form</h1>
    <form>
      {{diagnostic}}
      <div class="form-group">
        <label for="name">Website Name</label>
       &lt;input type="text" class="form-control" id="name"
         required
         [(ngModel)]="model.name" name="name">
      </div>
      <div class="form-group">
        <label for="alexa">Alexa Rank</label>
         &lt;input type="text"  class="form-control" id="alexa"
         [(ngModel)]="model.alexa" name="alexa">
      </div>
<div class="form-group">
  <label for="url">Website URL</label>
  &lt;select class="form-control" id="url"
          required
          [(ngModel)]="model.url" name="url">
    &lt;option *ngFor="let p of urls" [value]="p">{{p}}</option>
  </select>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>

-

Each input element has an id attribute, which is used by the label element's for attribute to match the label to the corresponding input.

-

Each input element has a name attribute, which is required by Angular's form module to register the control with the form.

Running the above example outputs the following result:

{{diagnostic}} is only used for outputting data during testing.

We can also track the modification status and validity of the form through ngModel, which uses three CSS classes to update the control to reflect the current state.

State Class if true Class if false
Control has been visited ng-touched ng-untouched
Control value has changed ng-dirty ng-pristine
Control value is valid ng-valid ng-invalid

This allows us to add custom CSS to reflect the form's state.

Create a forms.css file in the angular-forms directory with the following code:

forms.css file:

.ng-valid[required], .ng-valid.required  {
  border-left: 5px solid #42A948; /* green */
}

.ng-invalid:not(form)  {
  border-left: 5px solid #a94442; /* red */
}

Open the index.html file and add the following style link to the <head>:

<link rel="stylesheet" href="forms.css">

Modify app/site-form.component.html, as shown below:

app/site-form.component.html file:

<div class="container">
  <h1>Website Form</h1>
  <form>
    {{diagnostic}}
    <div class="form-group">
      <label for="name">Website Name</label>
      &lt;input type="text" class="form-control" id="name"
             required
             [(ngModel)]="model.name" name="name"
             #name="ngModel">
      &lt;div [hidden]="name.valid || name.pristine"
           class="alert alert-danger">
        Website name is required
      </div>
    </div>
    <div class="form-group">
      <label for="alexa">Alexa Rank</label>
      &lt;input type="text" class="form-control" id="alexa"
             [(ngModel)]="model.alexa" name="alexa">
    </div>
    <div class="form-group">
      <label for="url">Website URL</label>
      &lt;select class="form-control" id="url"
              required
              [(ngModel)]="model.url" name="url">
        &lt;option *ngFor="let p of urls" [value]="p">{{p}}</option>
      </select>
    </div>
    <button type="submit" class="btn btn-default">Submit</button>
  </form>
</div>
&lt;option *ngFor="let p of urls" [value]="p">{{p}}</option>
</select>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>

By binding the hidden attribute of the div element to the name control's property, we can control the visibility of the "name" field error message.

Remove the data from the name field, and the result is shown as follows:

Add a Website

Next, we create a form for adding websites, and add a button in app/site-form.component.html:

app/site-form.component.html File:

<button type="button" class="btn btn-default" (click)="newSite()">Add Website</button>

Bind the above button event to the component method:

app/site-form.component.ts File:

active = true;

newSite() {
  this.model = new Site(5, '', '');
  this.active = false;
  setTimeout(() => this.active = true, 0);
}

We add an active flag to the component and initialize it to true. When we add a new website, it sets the active flag to false, and then quickly sets it back to true via a quick setTimeout function.

Submit the Form with ngSubmit

We can use Angular's NgSubmit directive to submit the form and bind it to the SiteFormComponent.submit() method via event binding.

&lt;form *ngIf="active" (ngSubmit)="onSubmit()" #siteForm="ngForm">

We define a template reference variable #siteForm and initialize it to "ngForm".

This siteForm variable now references the NgForm directive, which represents the entire form.

The complete code for site-form.component.ts is as follows:

app/site-form.component.ts File:

import { Component } from '@angular/core';
import { Site }    from './site';

@Component({
  moduleId: module.id,
  selector: 'site-form',
  templateUrl: 'site-form.component.html'
})
export class SiteFormComponent {
  urls = ['www.tutorialpro.org', 'www.google.com',
            'www.taobao.com', 'www.facebook.com'];
  model = new Site(1, 'tutorialpro.org', this.urls[0], 10000);
  submitted = false;
  onSubmit() { this.submitted = true; }
  // TODO: Remove after completion
  get diagnostic() { return JSON.stringify(this.model); }
  active = true;
  newSite() {
    this.model = new Site(5, '', '');
    this.active = false;
    setTimeout(() => this.active = true, 0);
  }
}

The complete code for app/site-form.component.html is as follows:

app/site-form.component.html File:

<div class="container">
  &lt;div [hidden]="submitted">
    <h1>Website Form</h1>
    &lt;form *ngIf="active" (ngSubmit)="onSubmit()" #siteForm="ngForm">
      {{diagnostic}}
      <div class="form-group">
<label for="name">Website Name</label>
&lt;input type="text" class="form-control" id="name"
       required
       [(ngModel)]="model.name" name="name"
       #name="ngModel">
&lt;div [hidden]="name.valid || name.pristine" 
     class="alert alert-danger">
  Website Name is required
</div>
</div>
<div class="form-group">
<label for="alexa">Alexa Rank</label>
&lt;input type="text" class="form-control" id="alexa"
       [(ngModel)]="model.alexa" name="alexa">
</div>
<div class="form-group">
<label for="url">Website URL</label>
&lt;select class="form-control" id="url"
        required
        [(ngModel)]="model.url" name="url">
  &lt;option *ngFor="let p of urls" [value]="p">{{p}}</option>
</select>
</div>
<button type="submit" class="btn btn-default" [disabled]="!siteForm.form.valid">Submit</button>
<button type="button" class="btn btn-default" (click)="newSite()">Add Website</button>
</form>
</div>
&lt;div [hidden]="!submitted">
<h2>Your submitted information is as follows:</h2>
<div class="row">
  <div class="col-xs-3">Website Name</div>
  <div class="col-xs-9 pull-left">{{ model.name }}</div>
</div>
<div class="row">
  <div class="col-xs-3">Website Alexa Rank</div>
  <div class="col-xs-9 pull-left">{{ model.alexa }}</div>
</div>
<div class="row">
  <div class="col-xs-3">Website URL</div>
  <div class="col-xs-9 pull-left">{{ model.url }}</div>
</div>
<br>
<button class="btn btn-default" (click)="submitted=false">Edit</button>
</div>
</div>

In the template, we bind the hidden attribute to the SiteFormComponent.submitted property.

The main form is visible from the start because the submitted property is false, and it will be hidden after submission when the submitted property is true:

submitted = false;

onSubmit() { this.submitted = true; }

The final directory structure is:

Source Code Download

The complete example demonstration GIF is as follows: ```

❮ Angularjs2 Tutorial Home ❯