` with `*ngFor`.
The trick lies in knowing how to write the `*ngFor`. There are three key points:
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:
+makeExample('reactive-forms/ts/src/app/hero-detail-8.component.html', 'form-array-skeleton','src/app/hero-detail.component.html (*ngFor)')(format=".")
:marked
Here's the complete template for the _secret lairs_ section:
+makeExample('reactive-forms/ts/src/app/hero-detail-8.component.html', 'form-array','src/app/hero-detail.component.html (excerpt)')
:marked
### Add a new lair to the _FormArray_
Add an `addLair` method that gets the _secretLairs_ `FormArray` and appends a new _address_ `FormGroup` to it.
+makeExample('reactive-forms/ts/src/app/hero-detail-8.component.ts', 'add-lair','src/app/hero-detail.component.ts (addLair method)')(format=".")
:marked
Place a button on the form so the user can add a new _secret lair_ and wire it to the component's `addLair` method.
+makeExample('reactive-forms/ts/src/app/hero-detail-8.component.html', 'add-lair','src/app/hero-detail.component.html (addLair button)')(format=".")
.alert.is-important
:marked
Be sure to **add the `type="button"` attribute**.
In fact, you should always specify a button's `type`.
Without an explict 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.
:marked
### 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.
figure.image-display
img(src="/resources/images/devguide/reactive-forms/addresses-array.png" width="400px" alt="JSON output of addresses array")
:marked
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.
.l-main-section
a#observe-control
:marked
## 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 _secret lairs_.
Fortunately, you can learn about such changes by subscribing to one of the form control 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`.
+makeExample('reactive-forms/ts/src/app/hero-detail.component.ts', 'log-name-change','src/app/hero-detail.component.ts (logNameChange)')(format=".")
:marked
Call it in the constructor, after creating the form.
+makeExample('reactive-forms/ts/src/app/hero-detail-8.component.ts', 'ctor')(format=".")
:marked
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:
+makeExample('reactive-forms/ts/src/app/hero-detail.component.html', 'name-change-log','src/app/hero-detail.component.html (Name change log)')(format=".")
:marked
Return to the browser, select a hero (e.g, "Magneta"), and start typing in the _name_ input box.
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 form control property is handy for triggering
application logic _within_ the component class.
.l-main-section
a#save
:marked
## 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.
In a real app, you'd also be able to revert unsaved changes and resume editing.
After you implement both features in this section, the form will look like this:
figure.image-display
img(src="/resources/images/devguide/reactive-forms/save-revert-buttons.png" width="389px" alt="Form with save & revert buttons")
:marked
### Save
In this sample application, 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`.
+makeExample('reactive-forms/ts/src/app/hero-detail.component.ts', 'on-submit','src/app/hero-detail.component.ts (onSubmit)')(format=".")
:marked
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.
+makeExample('reactive-forms/ts/src/app/hero-detail.component.ts', 'prepare-save-hero','src/app/hero-detail.component.ts (prepareSaveHero)')(format=".")
.l-sub-section
:marked
**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.
:marked
### 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 `ngOnChanges` method that built the _form model_ from the original, unchanged `hero` _data model_.
+makeExample('reactive-forms/ts/src/app/hero-detail.component.ts', 'revert','src/app/hero-detail.component.ts (revert)')(format=".")
:marked
### Buttons
Add the "Save" and "Revert" buttons near the top of the component's template:
+makeExample('reactive-forms/ts/src/app/hero-detail.component.html', 'buttons','src/app/hero-detail.component.html (Save and Revert buttons)')(format=".")
:marked
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.
This is the final step in the demo.
Try the .
.l-main-section
:marked
## Conclusion
This page covered:
- How to create a reactive form component and its corresponding template.
- How to use `FormBuilder` to simplify coding a reactive form.
- Grouping `FormControls`.
- Inspecting `FormControl` properties.
- Setting data with `patchValue` and `setValue`.
- Adding groups dynamically with `FormArray`.
- Observing changes to the value of a `FormControl`.
- Saving form changes.
a#source-code
:marked
The key files of the final version are as follows:
+makeTabs(
`reactive-forms/ts/src/app/app.component.ts,
reactive-forms/ts/src/app/app.module.ts,
reactive-forms/ts/src/app/hero-detail.component.ts,
reactive-forms/ts/src/app/hero-detail.component.html,
reactive-forms/ts/src/app/hero-list.component.html,
reactive-forms/ts/src/app/hero-list.component.ts,
reactive-forms/ts/src/app/data-model.ts,
reactive-forms/ts/src/app/hero.service.ts
`,
'',
`src/app/app.component.ts,
src/app/app.module.ts,
src/app/hero-detail.component.ts,
src/app/hero-detail.component.html,
src/app/hero-list.component.html,
src/app/hero-list.component.ts,
src/app/data-model.ts,
src/app/hero.service.ts,
`)
:marked
You can download the complete source for all steps in this guide
from the Reactive Forms Demo live example.