`.
Add a `formGroupName` directive to the `div` and bind it to `"address"`.
That's the property of the `address` child `FormGroup` within the parent `FormGroup` called `heroForm`. Leave the `
` with the `name` `
`.
To make this change visually obvious, add an `
` header near the top with the text, _Secret Lair_.
The new address HTML looks like this:
After these changes, the JSON output in the browser shows the revised form model
with the nested address `FormGroup`:
This shows that the template
and the form model are talking to one another.
{@a properties}
## Inspect `FormControl` Properties
You can inspect an individual `FormControl` within a form by extracting it with the `get()` method.
You can do this within the component class or display it on the
page by adding the following to the template,
immediately after the `{{form.value | json}}` interpolation as follows:
To get the state of a `FormControl` that’s inside a `FormGroup`, use dot notation to traverse to the control.
*Note*: If you're coding along, remember to remove this reference to `address.street` when you get to the section on `FormArray`. In that section, you change the name of address in the component class and it will throw an error if you leave it in the template.
You can use this technique to display any property of a `FormControl`
such as one of the following:
Property
Description
myControl.value
the value of a `FormControl`.
myControl.status
the validity of a `FormControl`. Possible values: `VALID`,
`INVALID`, `PENDING`, or `DISABLED`.
myControl.pristine
`true` if the user has _not_ changed the value in the UI.
Its opposite is `myControl.dirty`.
myControl.untouched
`true` if the control user has not yet entered the HTML control
and triggered its blur event. Its opposite is `myControl.touched`.
Read about other `FormControl` properties in the
[_AbstractControl_](api/forms/AbstractControl) API reference.
One common reason for inspecting `FormControl` properties is to
make sure the user entered valid values.
Read more about validating Angular forms in the
[Form Validation](guide/form-validation) guide.
{@a data-model-form-model}
## The data model and the form model
At the moment, the form is displaying empty values.
The `HeroDetailComponent` should display values of a hero,
possibly a hero retrieved from a remote server.
In this app, the `HeroDetailComponent` gets its hero from a parent `HeroListComponent`.
The `hero` from the server is the **_data model_**.
The `FormControl` structure is the **_form model_**.
The component must copy the hero values in the data model into the form model.
There are two important implications:
1. The developer must understand how the properties of the data model
map to the properties of the form model.
2. User changes flow from the DOM elements to the form model, not to the data model.
The form controls never update the _data model_.
The form and data model structures don't need to match exactly.
You often present a subset of the data model on a particular screen.
But it makes things easier if the shape of the form model is close to the shape of the data model.
In this `HeroDetailComponent`, the two models are quite close.
Here are the definitions of `Hero` and `Address` in `data-model.ts`:
Here, again, is the component's `FormGroup` definition.
There are two significant differences between these models:
1. The `Hero` has an `id`. The form model does not because you generally don't show primary keys to users.
1. The `Hero` has an array of addresses. This form model presents only one address,
which is covered in the section on [`FormArray`](guide/reactive-forms#form-array "Form arrays") below.
Keeping the two models close in shape facilitates copying the data model properties
to the form model with the `patchValue()` and `setValue()` methods in the next section.
First, refactor the `address` `FormGroup` definition as follows:
Also be sure to update the `import` from `data-model` so you can reference the `Hero` and `Address` classes:
{@a set-data}
## Populate the form model with `setValue()` and `patchValue()`
*Note*: If you're coding along, this section is optional as the rest of the steps do not rely on it.
Previously, you created a control and initialized its value at the same time.
You can also initialize or reset the values later with the
`setValue()` and `patchValue()` methods.
### `setValue()`
With `setValue()`, you assign every form control value at once
by passing in a data object whose properties exactly match the form model behind the `FormGroup`.
The `setValue()` method checks the data object thoroughly before assigning any form control values.
It will not accept a data object that doesn't match the `FormGroup` structure or is
missing values for any control in the group. This way, it can return helpful
error messages if you have a typo or if you've nested controls incorrectly.
Conversely, `patchValue()` will fail silently.
Notice that you can almost use the entire `hero` as the argument to `setValue()`
because its shape is similar to the component's `FormGroup` structure.
You can only show the hero's first address and you must account for the possibility that the `hero` has no addresses at all, as in the conditional setting of the `address` property in the data object argument:
### `patchValue()`
With **`patchValue()`**, you can assign values to specific controls in a `FormGroup`
by supplying an object of key/value pairs for them.
This example sets only the form's `name` control.
With `patchValue()` you have more flexibility to cope with divergent data and form models.
But unlike `setValue()`, `patchValue()` cannot check for missing control
values and doesn't throw helpful errors.
{@a hero-list}
## Create the `HeroListComponent` and `HeroService`
To demonstrate further reactive forms techniques, it is helpful to add more functionality to the example by adding a `HeroListComponent` and a `HeroService`.
The `HeroDetailComponent` is a nested sub-component of the `HeroListComponent` in a _master/detail_ view. Together they look like this:
First, add a `HeroListComponent` with the following command:
ng generate component HeroList
Give the `HeroListComponent` the following contents:
Next, add a `HeroService` using the following command:
ng generate service Hero
Then, give it the following contents:
The `HeroListComponent` uses an injected `HeroService` to retrieve heroes from the server
and then presents those heroes to the user as a series of buttons.
The `HeroService` emulates an HTTP service.
It returns an `Observable` of heroes that resolves after a short delay,
both to simulate network latency and to indicate visually
the necessarily asynchronous nature of the application.
When the user clicks on a hero,
the component sets its `selectedHero` property which
is bound to the `hero` `@Input()` property of the `HeroDetailComponent`.
The `HeroDetailComponent` detects the changed hero and resets its form
with that hero's data values.
A refresh button clears the hero list and the current selected hero before refetching the heroes.
Notice that `hero-list.component.ts` imports `Observable` and the `finalize` operator, while `hero.service.ts` imports `Observable`, `of`, and the `delay` operator from `rxjs`.
The remaining `HeroListComponent` and `HeroService` implementation details are beyond the scope of this tutorial.
However, the techniques involved are covered elsewhere in the documentation, including the _Tour of Heroes_
[here](tutorial/toh-pt3 "ToH: Multiple Components") and [here](tutorial/toh-pt4 "ToH: Services").
To use the `HeroService`, import it into `AppModule` and add it to the `providers` array. To use the `HeroListComponent`, import it, declare it, and export it:
Next, update the `HeroListComponent` template with the following:
These changes need to be reflected in the `AppComponent` template. Replace the contents of `app.component.html` with updated markup to use the `HeroListComponent`, instead of the `HeroDetailComponent`:
Finally, add an `@Input()` property to the `HeroDetailComponent`
so `HeroDetailComponent` can receive the data from `HeroListComponent`. Remember to add the `Input` symbol to the `@angular/core ` `import` statement in the list of JavaScript imports too.
Now you should be able to click on a button for a hero and a form renders.
## When to set form model values (`ngOnChanges`)
When to set form model values depends upon when the component gets the data model values.
The `HeroListComponent` displays hero names to the user.
When the user clicks on a hero, the `HeroListComponent` passes the selected hero into the `HeroDetailComponent`
by binding to its `hero` `@Input()` property.
In this approach, the value of `hero` in the `HeroDetailComponent` changes
every time the user selects a new hero.
You can call `setValue()` using the [ngOnChanges](guide/lifecycle-hooks#onchanges)
lifecycle hook, which Angular calls whenever the `@Input()` `hero` property changes.
### Reset the form
First, import the `OnChanges` symbol in `hero-detail.component.ts`.
Next, let Angular know that the `HeroDetailComponent` implements `OnChanges`:
Add the `ngOnChanges` method to the class as follows:
Notice that it calls `rebuildForm()`, which is a method where you
can set the values. You can name `rebuildForm()` anything that makes sense to you. It isn't built into Angular, but is a method you create to effectively leverage the `ngOnChanges` lifecycle hook.
The `rebuildForm()` method does two things; resets the hero's name and the address.
{@a form-array}
## Use _FormArray_ to present an array of `FormGroups`
A `FormGroup` is a named object whose property values are `FormControls` and other `FormGroups`.
Sometimes you need to present an arbitrary number of controls or groups.
For example, a hero may have zero, one, or any number of addresses.
The `Hero.addresses` property is an array of `Address` instances.
An `address` `FormGroup` can display one `Address`.
An Angular `FormArray` can display an array of `address` `FormGroups`.
To get access to the `FormArray` class, import it into `hero-detail.component.ts`:
To work with a `FormArray` do the following:
1. Define the items in the array; that is, `FormControls` or `FormGroups`.
1. Initialize the array with items created from data in the data model.
1. Add and remove items as the user requires.
Define a `FormArray` for `Hero.addresses` and
let the user add or modify addresses.
You’ll need to redefine the form model in the `HeroDetailComponent` `createForm()` method,
which currently only displays the first hero address in an `address` `FormGroup`:
### From `address` to `secretLairs`
From the user's point of view, heroes don't have _addresses_.
Addresses are for mere mortals. Heroes have _secret lairs_!
Replace the address `FormGroup` definition with a `secretLairs` `FormArray` definition:
In `hero-detail.component.html` change `formGroupName="address"` to `formArrayName="secretLairs"`.
Changing the form control name from `address` to `secretLairs` underscores an important point:
the _form model_ doesn't have to match the _data model_.
Obviously, there has to be a relationship between the two.
But it can be anything that makes sense within the application domain.
_Presentation_ requirements often differ from _data_ requirements.
The reactive forms approach both emphasizes and facilitates this distinction.
### Initialize the `secretLairs` _FormArray_
The default form displays a nameless hero with no addresses.
You need a method to populate (or repopulate) the `secretLairs` with actual hero addresses whenever
the parent `HeroListComponent` sets the `HeroDetailComponent.hero` `@Input()` property to a new `Hero`.
The following `setAddresses()` method replaces the `secretLairs` `FormArray` with a new `FormArray`,
initialized by an array of hero address `FormGroups`. Add this to the `HeroDetailComponent` class:
Notice that you replace the previous `FormArray` with the
`FormGroup.setControl()` method, not with `setValue()`.
You're replacing a _control_, not the _value_ of a control.
Notice also that the `secretLairs` `FormArray` contains `FormGroups`, not `Addresses`.
Next, call `setAddresses()` from within `rebuildForm()`:
### Get the _FormArray_
The `HeroDetailComponent` should be able to display, add, and remove items from the `secretLairs` `FormArray`.
Use the `FormGroup.get()` method to acquire a reference to that `FormArray`.
Wrap the expression in a `secretLairs` convenience property for clarity and re-use. Add the following to `HeroDetailComponent`.
### Display the _FormArray_
The current HTML template displays a single `address` `FormGroup`.
Revise it to display zero, one, or more of the hero's `address` `FormGroups`.
This is mostly a matter of wrapping the previous template HTML for an address in a `
` and
repeating that `
` with `*ngFor`.
There are three key points when writing the `*ngFor`:
1. Add another wrapping `
`, around the `
` with `*ngFor`, and
set its `formArrayName` directive to `"secretLairs"`.
This step establishes the `secretLairs` `FormArray` as the context for form controls in the inner, repeated HTML template.
1. The source of the repeated items is the `FormArray.controls`, not the `FormArray` itself.
Each control is an `address` `FormGroup`, exactly what the previous (now repeated) template HTML expected.
1. Each repeated `FormGroup` needs a unique `formGroupName`, which must be the index of the `FormGroup` in the `FormArray`.
You'll re-use that index to compose a unique label for each address.
Here's the skeleton for the secret lairs section of the HTML template:
Here's the complete template for the secret lairs section. Add this to `HeroDetailComponent` template, replacing the `forGroupName=address` `
`:
### Add a new lair to the _FormArray_
Add an `addLair()` method that gets the `secretLairs` `FormArray` and appends a new `address` `FormGroup` to it.
Place a button on the form so the user can add a new _secret lair_ and wire it to the component's `addLair()` method. Put it just before the closing `
` of the `secretLairs` `FormArray`.
Be sure to add the `type="button"` attribute
because without an explicit type, the button type defaults to "submit".
When you later add a form submit action, every "submit" button triggers the submit action which
might do something like save the current changes.
You do not want to save changes when the user clicks the _Add a Secret Lair_ button.
### Try it!
Back in the browser, select the hero named "Magneta".
"Magneta" doesn't have an address, as you can see in the diagnostic JSON at the bottom of the form.
Click the "_Add a Secret Lair_" button.
A new address section appears. Well done!
### Remove a lair
This example can _add_ addresses but it can't _remove_ them.
For extra credit, write a `removeLair` method and wire it to a button on the repeating address HTML.
{@a observe-control}
## Observe control changes
Angular calls `ngOnChanges()` when the user picks a hero in the parent `HeroListComponent`.
Picking a hero changes the `HeroDetailComponent.hero` `@Input()` property.
Angular does _not_ call `ngOnChanges()` when the user modifies the hero's `name` or `secretLairs`.
Fortunately, you can learn about such changes by subscribing to one of the `FormControl` properties
that raises a change event.
These are properties, such as `valueChanges`, that return an RxJS `Observable`.
You don't need to know much about RxJS `Observable` to monitor form control values.
Add the following method to log changes to the value of the `name` `FormControl`.
Call it in the constructor, after `createForm()`.
The `logNameChange()` method pushes name-change values into a `nameChangeLog` array.
Display that array at the bottom of the component template with this `*ngFor` binding:
Return to the browser, select a hero; for example, Magneta, and start typing in the `name` `
`.
You should see a new name in the log after each keystroke.
### When to use it
An interpolation binding is the easier way to display a name change.
Subscribing to an observable `FormControl` property is handy for triggering
application logic within the component class.
{@a save}
## Save form data
The `HeroDetailComponent` captures user input but it doesn't do anything with it.
In a real app, you'd probably save those hero changes, revert unsaved changes, and resume editing.
After you implement both features in this section, the form will look like this:
### Save
When the user submits the form,
the `HeroDetailComponent` will pass an instance of the hero _data model_
to a save method on the injected `HeroService`. Add the following to `HeroDetailComponent`.
This original `hero` had the pre-save values. The user's changes are still in the _form model_.
So you create a new `hero` from a combination of original hero values (the `hero.id`)
and deep copies of the changed form model values, using the `prepareSaveHero()` helper.
Make sure to import `HeroService` and add it to the constructor:
**Address deep copy**
Had you assigned the `formModel.secretLairs` to `saveHero.addresses` (see line commented out),
the addresses in `saveHero.addresses` array would be the same objects
as the lairs in the `formModel.secretLairs`.
A user's subsequent changes to a lair street would mutate an address street in the `saveHero`.
The `prepareSaveHero` method makes copies of the form model's `secretLairs` objects so that can't happen.
### Revert (cancel changes)
The user cancels changes and reverts the form to the original state by pressing the Revert button.
Reverting is easy. Simply re-execute the `rebuildForm()` method that built the form model from the original, unchanged `hero` data model.
### Buttons
Add the "Save" and "Revert" buttons near the top of the component's template:
The buttons are disabled until the user "dirties" the form by changing a value in any of its form controls (`heroForm.dirty`).
Clicking a button of type `"submit"` triggers the `ngSubmit` event which calls the component's `onSubmit` method.
Clicking the revert button triggers a call to the component's `revert` method.
Users now can save or revert changes.
Try the
.
{@a source-code}
The key files of the final version are as follows:
You can download the complete source for all steps in this guide
from the
Reactive Forms Demo live example.