Over a million developers have joined DZone.

Angular 2 Forms — A First Look

In this post I'm going to sum up a recent webinar with Kara Erickson, who explained how Angular 2's Form API works. I'll also provide you with a runnable Plunker to play with.

· Web Dev Zone

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

Angular 2 gets a brand new Forms API. It's fresh out of the compiler and since there's not much documentation around yet, @angular_zone powered by Gerard Sans and Wassim Chegham organized a Google Hangout with Kara Erickson, core contributor to the Forms API. Here, I'm basically summarizing the main points and created a runnable Plunker for you to play with.

There are basically two different approaches to Angular 2 forms:

  • Template driven, Angular 1 style forms
  • Reactive, or model-driven forms

Both of them are feature equivalent, so you’re fine to go either way. It’s however not suggested to mix them. Also, both share the same underlying concepts:

View

<input type="text" />


Model, which has properties for validation like

  • value
  • valid/invalid
  • pristine/dirty
  • touched/untouched
  • errors

It’s important to note that this is not the domain model you bind through ngModel or similar. It’s a dedicated form model holding the validation and form control state properties.

So, the view part is what is called value accessor because we may have different kind of views with different ways of getting hold of their respective value:

<input type="text" />
<input type="radio" />
<select></select>

The value accessor knows how to interact with the underlying DOM element to get and set the according value. All of them implement the ControlValueAccessor interface.

The model part is an instance of FormControl.

Every form control has one view (ControlValueAccessor) and a model instance (FormControl).

So to summarize:

  • Template driven
    • Elements like ngModel, ngModelGroup, ngForm
    • implicitly created
    • asynchronous, lots of stuff going on behind the scenes for wiring up the FormControl instance etc.
  • Reactive (model-driven)
    • Elements like formControlName, formGroupName, formArrayName, formControl, formGroup
    • explicitly/programmatically created
    • synchronous and more predictive as they’re created programmatically and there’s not template rendering in the middle

Let’s take a look.

Enabling the Form API

Before starting straight into form coding, we need to disable the deprecated form support (disableDeprecatedForms()), and enable the brand new Forms API (provideForms()) on our Angular 2 app. Open your main file where you bootstrap the app.

// main entry point
import {bootstrap} from '@angular/platform-browser-dynamic';
import {disableDeprecatedForms, provideForms} from '@angular/forms';

import {App} from './app';

bootstrap(App, [
  disableDeprecatedForms(),
  provideForms()
  ])
  .catch(err => console.error(err));

Now we should be good to go. Here’s a runnable Plunker which you can use to test out the various concepts as we quickly go over them.

Angular2 Forms Api demo - templated based approach


Template Driven Approach

So assume we’re having this plain simple HTML form:

<form>
  <div>
    Firstname: <input type="text" name="firstname" />
  </div>
  <div>
    Surname: <input type="text" name="surname" />
  </div>
  <div>
    Age: <input type="text" name="Age" />
  </div>
</form>

To hook it to Angular 2’s Forms API, you first need to declare a #form like

<form #form="ngForm">
  ...
</form>

Simple Bindings

As a next step we need to add the ngModel attribute to our form controls:

<input type="text" ngModel name="firstname" />

This way we use the template driven approach, meaning by adding the ngModel attribute, behind the scenes the FormControl instance is created for use which we need for getting access to the underlying DOM element value. To see this two-way binding works, let’s print out the state of our form value:

<pre>{{ form.value | json }}</pre>

By typing into the text fields you can now see how we established a proper binding. Check it out yourself in the linked Plunker.

Grouping Form Controls

Often it is necessary to group a number of form controls together, like the concept of an address, having properties such as street, city, state, and zipcode. All of these are represented as separate form controls, but can be bound together as a form group.

So, what’s the effect of that? Well, these form groups get all of the same properties as the individual form controls have (like value, validity, touched/untouched, etc.), but they are derived from the value of their children. For instance, if one child is invalid, the whole group becomes invalid.

Moreover, the fields of a FormGroup get serialized into a dedicated object (we’ll explore soon what that looks like). A variant of that is the FormArray. The key difference is that its data gets serialized as an array. This might be especially useful when you don’t know how many controls will be present within the group, like dynamic forms.

Okay so far? Good. Consider the concept of an address. We can simply add more fields to our form as follows:

<form #form="ngForm">
  <div>
    Firstname: <input type="text" ngModel name="firstname" />
  </div>
  <div>
    Surname: <input type="text" ngModel name="surname" />
  </div>
  <div>
    Age: <input type="text" ngModel name="age" />
  </div>
  <fieldset>
    <legend>Address</legend>
    <div>
      Street: <input type="text" ngModel name="street" />
    </div>
    <div>
      ZipCode: <input type="text" ngModel name="zipCode" />
    </div>
  </fieldset>
</form>

This would get serialized like:

{
  "firstname": "",
  "surname": "",
  "age": "",
  "street": "",
  "zipCode": ""
}

But given that the address is already logically grouped on our UI, we want it to be serialized into a separate object address. As we just learned, this is as simple as adding a ngModelGroup:

<form #form="ngForm">
  ...
  <fieldset ngModelGroup="address">
    <legend>Address</legend>
    <div>
      Street: <input type="text" ngModel name="street" />
    </div>
    <div>
      ZipCode: <input type="text" ngModel name="zipCode" />
    </div>
  </fieldset>
</form>

The resulting serialized object in turn now looks like this:

{
  "firstname": "",
  "surname": "",
  "age": "",
  "address": {
    "street": "",
    "zipCode": ""
  }
}

Reactive Approach

We’ve seen the template-based approach. Let’s take a look at the reactive or model-based way of writing Forms. Again, here’s a Plunker to play with:

Angular2 Forms Api demo - model based approach

Given it’s model-based, we start from the JavaScript code of our component

...
import {FormControl, FormGroup} from '@angular/forms';

@Component({
  ...
})
export class App {
  form = new FormGroup({
    firstname: new FormControl(),
    surname: new FormControl(),
    age: new FormControl(),
    address: new FormGroup({
      street: new FormControl(),
      zipCode: new FormControl()
    })
  });
}

Note how we construct our controls and groups programmatically using the FormControl and FormGroup classes. Next, we need to hook this form to our template:

import {REACTIVE_FORM_DIRECTIVES} from '@angular/forms';

@Component({
  template: `
    <div>
      <h2>Forms Api demo</h2>
      <form [formGroup]="form">
        ...
      </form>
    </div>
  `,
  directives: [REACTIVE_FORM_DIRECTIVES]
})
export class App { ... }

We do this by using the [formGroup] binding. Also note, that for doing so we need to add the REACTIVE_FORM_DIRECTIVES to the directives array of our component.

And finally, we need to map the group and form controls using formControlName and formGroupName.

<form [formGroup]="form">
  <div>
    Firstname: <input type="text" formControlName="firstname" />
  </div>
  <div>
    Surname: <input type="text" formControlName="surname" />
  </div>
  <div>
    Age: <input type="text" formControlName="age" />
  </div>
  <fieldset formGroupName="address">
    <legend>Address</legend>
    <div>
      Street: <input type="text" formControlName="street" />
    </div>
    <div>
      ZipCode: <input type="text" formControlName="zipCode" />
    </div>
  </fieldset>
</form>

Wrapping up

Great, so I hope I was able to give you a good first overview of what the Forms API looks like in Angular 2. Obviously, there’s much more to explore!

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

Topics:
forms ,dom ,angular ,api

Published at DZone with permission of Juri Strumpflohner, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

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

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

{{ parent.tldr }}

{{ parent.urlSource.name }}