Forms in Angular

Forms are Crucial, Forms are Complex

Forms are probably the most crucial aspect of your web application. While we often get events from clicking on links or moving the mouse, it’s through forms where we get the majority of our rich data input from users.

On the surface, forms seem straightforward: you make an input tag, the user fills it out, and hits submit. How hard could it be?

It turns out, forms can be very complex. Here’s a few reasons why:

Thankfully, Angular has tools to help with all of these things.

In this chapter we’re going to walk through building forms, step by step. We’ll start with some simple forms and build up to more complicated logic.

FormControls and FormGroups

The two fundamental objects in Angular forms are FormControl and FormGroup.

FormControl

A FormControl represents a single input field - it is the smallest unit of an Angular form.

FormControls encapsulate the field’s value, and states such as being valid, dirty (changed), or has errors.

For instance, here’s how we might use a FormControl in TypeScript:

{lang=javascript,line-numbers=off}
// create a new FormControl with the value “Nate” let nameControl = new FormControl(“Nate”);

  let name = nameControl.value; // -> Nate

// now we can query this control for certain values:
nameControl.errors // -> StringMap<string, any> of errors
nameControl.dirty  // -> false
nameControl.valid  // -> true
// etc.

To build up forms we create FormControls (and groups of FormControls) and then attach metadata and logic to them.

Like many things in Angular, we have a class (FormControl, in this case) that we attach to the DOM with an attribute (formControl, in this case). For instance, we might have the following in our form:

{lang=html,line-numbers=off}
<input type=”text” [formControl]=”name” />

This will create a new FormControl object within the context of our form. We’ll talk more about how that works below.

FormGroup

Most forms have more than one field, so we need a way to manage multiple FormControls. If we wanted to check the validity of our form, it’s cumbersome to iterate over an array of FormControls and check each FormControl for validity. FormGroups solve this issue by providing a wrapper interface around a collection of FormControls.

Here’s how you create a FormGroup:

{lang=javascript,line-numbers=off}
let personInfo = new FormGroup({ firstName: new FormControl(“Nate”), lastName: new FormControl(“Murray”), zip: new FormControl(“90210”) })

FormGroup and FormControl have a common ancestor (AbstractControl). That means we can check the status or value of personInfo just as easily as a single FormControl:

{lang=javascript,line-numbers=off}
personInfo.value; // -> { // firstName: “Nate”, // lastName: “Murray”, // zip: “90210” // }

  // now we can query this control group for certain values, which have sensible
// values depending on the children FormControl's values:
personInfo.errors // -> StringMap<string, any> of errors
personInfo.dirty  // -> false
personInfo.valid  // -> true
// etc.

Notice that when we tried to get the value from the FormGroup we received an object with key-value pairs. This is a really handy way to get the full set of values from our form without having to iterate over each FormControl individually.

Our First Form

There are lots of moving pieces to create a form, and several important ones we haven’t touched on. Let’s jump in to a full example and I’ll explain each piece as we go along.

You can find the full code listing for this section in the code download under forms/

Here’s a screenshot of the very first form we’re going to build:

logo

In our imaginary application we’re creating an e-commerce-type site where we’re listing products for sale. In this app we need to store the product’s SKU, so let’s create a simple form that takes the SKU as the only input field.

SKU is an abbreviation for “stockkeeping unit”. It’s a term for a unique id for a product that is going to be tracked in inventory. When we talk about a SKU, we’re talking about a human-readable item ID.

Our form is super simple: we have a single input for sku (with a label) and a submit button.

Let’s turn this form into a Component. If you recall, there are three parts to defining a component:

Let’s take these in turn:

Loading the FormsModule

In order to use the new forms library we need to first make sure we import the forms library in our NgModule.

There are two ways of using forms in Angular and we’ll talk about them both in this chapter: using FormsModule or using ReactiveFormsModule. Since we’ll use both, we’ll import them both into our module. To do this, we do the following in our app.ts where we bootstrap the app:

// app.module.ts
import {
  FormsModule,
  ReactiveFormsModule
} from '@angular/forms';
  // farther down...

@NgModule({
  declarations: [
    AppComponent,
    DemoFormSkuComponent,
    // ... our declarations here
  ],
  imports: [
    BrowserModule,
    FormsModule,         // <-- add this
    ReactiveFormsModule  // <-- and this
  ],
  bootstrap: [ AppComponent ]
})
class AppModule {}

This ensures that we’re able to use the form directives in our views. At the risk of jumping ahead, the FormsModule gives us template driven directives such as:

Whereas ReactiveFormsModule gives us reactive driven directives like

… and several more. We haven’t talked about how to use these directives or what they do, but we will shortly. For now, just know that by importing FormsModule and ReactiveFormsModule into our NgModule means we can use any of the directives in that list in our view template or inject any of their respective providers into our components.

Reactive- vs. template-driven Forms

Angular allows you to define forms in two different ways: “reactive” or “template” driven. You can see a comparison of two ways here. Rather than describe how they’re different, we’re going to show you examples of different ways you can build forms - then you can decide which is right for your application.

Simple SKU Form: @Component Decorator

First, let’s start by creating what’s called a “template driven” form. Starting with our component:

    import { Component, OnInit } from '@angular/core';
    
    @Component({
      selector: 'app-demo-form-sku',
      templateUrl: './demo-form-sku.component.html',

Here we define a selector of app-demo-form-sku. If you recall, selector tells Angular what elements this component will bind to. In this case we can use this component by having a app-demo-form-sku tag like so:

<app-demo-form-sku></app-demo-form-sku>

Simple SKU Form: template

Let’s look at our template:

    <div class="ui raised segment">
      <h2 class="ui header">Demo Form: Sku</h2>
      <form #f="ngForm"
            (ngSubmit)="onSubmit(f.value)"
            class="ui form">
    
        <div class="field">
          <label for="skuInput">SKU</label>
          <input type="text"
                 id="skuInput"
                 placeholder="SKU"
                 name="sku" ngModel>
        </div>
    
        <button type="submit" class="ui button">Submit</button>
      </form>
    </div>
 
This page is a preview of ng-book 2.
Get the rest of this chapter plus hundreds of pages Angular 7 instruction, 5 sample projects, a screencast, and more.

 

Ready to master Angular 7?

  • What if you could master the entire framework – with solid foundations – in less time without beating your head against a wall? Imagine how quickly you could work if you knew the best practices and the best tools?
  • Stop wasting your time searching and have everything you need to be productive in one, well-organized place, with complete examples to get your project up without needing to resort to endless hours of research.
  • You will learn what you need to know to work professionally with ng-book: The Complete Book on Angular 7 or get your money back.
Download the First Chapter (for free)