@@ -56,16 +56,9 @@ a#intro
56
56
the form controls and pull user-changed values back out. The component can
57
57
observe changes in form control state and react to those changes.
58
58
59
- In keeping with the reactive paradigm, the component
60
- preserves the immutability of the _data model_,
61
- treating it as a pure source of original values.
62
- Rather than update the data model directly,
63
- the component extracts user changes and forwards them to an external component or service,
64
- which does something with them (such as saving them)
65
- and returns a new _data model_ to the component that reflects the updated model state.
66
-
67
- Using reactive form directives does not require you to follow all reactive priniciples,
68
- but it does facilitate the reactive programming approach should you choose to use it.
59
+ One advantage of working with form control objects directly is that value and validity updates
60
+ are always synchronous and under your control.
61
+ You won't encounter the timing issues that sometimes plague a template-driven form.
69
62
70
63
### _Template-driven_ forms
71
64
@@ -542,55 +535,48 @@ a#set-data
542
535
543
536
Now you know _how_ to set the _form model_ values. But _when_ do you set them?
544
537
The answer depends upon when the component gets the _data model_ values.
545
-
546
- If the `HeroDetailComponent` is editing a _new_ hero, the data model consists entirely of default values
547
- that you could copy to the `FormControls` when you create them in the component's constructor.
548
- That's effectively what you did when you set each control to the empty string.
549
-
550
- If the `HeroDetailComponent` is editing an _existing_ hero, you have to wait for that hero to arrive.
551
- The component _might_ have the ability to retrieve the hero from the server (via an injected data service)
552
- in which case you could set the form model in the [ngOnInit](lifecyle-hooks.html#oninit) lifecycle hook.
553
- The final version of the `HeroDetailComponent` in the [Tour of Heroes](../tutorial/toh-pt6.html) does it this way.
554
538
555
- The `HeroDetailComponent` in this reactive forms sample works a little differently.
556
- It's the _detail_ view nested within a _master/detail_ `HeroListComponent` ([discussed below](#hero-list)).
557
-
539
+ The `HeroDetailComponent` in this reactive forms sample is nested within a _master/detail_ `HeroListComponent` ([discussed below](#hero-list)).
558
540
The `HeroListComponent` displays hero names to the user.
559
541
When the user clicks on a hero, the list component passes the selected hero into the `HeroDetailComponent`
560
- by ** binding to the latter's `hero` input property** .
542
+ by binding to its `hero` input property.
561
543
562
544
+ makeExample('reactive-forms/ts/app/hero-list.component.1.html' , '' ,'hero-list.component.html (simplified)' )( format ="." )
563
545
564
546
:marked
565
547
In this approach, the value of `hero` in the `HeroDetailComponent` changes
566
548
every time the user selects a new hero.
567
- You can't call _setValue_ in `ngOnInit` or you'll miss the user's selections.
568
- Put the _setValue_ logic in the [ngOnChanges](lifecyle-hooks.html#onchanges)
569
- hook, which Angular calls whenever the input `hero` property changes .
549
+ You should call _setValue_ in the [ngOnChanges](lifecyle-hooks.html#onchanges)
550
+ hook, which Angular calls whenever the input `hero` property changes
551
+ as the following steps demonstrate .
570
552
571
- You should also _reset the form_ when the hero changes so that
572
- control values from the previous hero are cleared and
573
- status flags are restored to the _pristine_ state.
553
+ First, import the `ngOnChanges` and `import` symbol in `hero-detail.component.ts`.
574
554
575
- In order to use `ngOnChanges`, add it to the `hero-detail.component.ts`
576
- imports.
555
+ + makeExample('reactive-forms/ts/app/hero-detail-5.component.ts' , 'import-input' ,'app/hero-detail.component.ts (core imports)' )( format ="." )
577
556
578
- + makeExample('reactive-forms/ts/app/hero-detail-5.component.ts' , 'imports' ,'app/hero-detail.component.ts (excerpt)' )( format ="." )
557
+ :marked
558
+ Add the `hero` input property.
559
+ + makeExample('reactive-forms/ts/app/hero-detail-5.component.ts' , 'hero' )( format ="." )
579
560
580
561
:marked
581
- Here's what `ngOnChanges` should look like :
562
+ Add the `ngOnChanges` method to the class as follows :
582
563
583
- + makeExample('reactive-forms/ts/app/hero-detail-6.component.ts' , 'set-value-on-changes ' ,'app/hero-detail.component.ts (excerpt )' )( format ="." )
564
+ + makeExample('reactive-forms/ts/app/hero-detail-6.component.ts' , 'ngOnChanges-1 ' ,'app/hero-detail.component.ts (ngOnchanges )' )( format ="." )
584
565
585
566
:marked
586
- You could, however, reset the value of the *formControls* in the `reset()`
587
- method by refactoring.
588
- ## Ward, are the benefits that now you can dispense with `setValue` (so less code
589
- ## and you don't have to set everything)? In `hero-detail-7.component.ts`, we also have
590
- ## `this.setAddresses(this.hero.addresses);` in `ngOnChanges`.
591
- ## Do I need to include it and talk about it?
567
+ ### _reset_ the form flags
568
+
569
+ You should reset the form when the hero changes so that
570
+ control values from the previous hero are cleared and
571
+ status flags are restored to the _pristine_ state.
572
+ You could call `reset` at the top of `ngOnChanges` like this.
573
+ + makeExample('reactive-forms/ts/app/hero-detail-6.component.ts' , 'reset' )( format ="." )
592
574
593
- + makeExample('reactive-forms/ts/app/hero-detail-7.component.ts' , 'reset-refactor' ,'app/hero-detail.component.ts (excerpt)' )( format ="." )
575
+ :marked
576
+ The `reset` method has an optional `state` value so you can reset the flags _and_ the control values at the same.
577
+ Internally, `reset` passes the argument to `setValue`.
578
+ A little refactoring and `ngOnChanges` becomes this:
579
+ + makeExample('reactive-forms/ts/app/hero-detail-6.component.ts' , 'ngOnChanges' , 'app/hero-detail.component.ts (ngOnchanges - revised)' )( format ="." )
594
580
595
581
a#hero-list
596
582
:marked
@@ -611,12 +597,11 @@ figure.image-display
611
597
612
598
When the user clicks on a hero,
613
599
the component sets its `selectedHero` property which
614
- is ** bound to the `hero` input property** of the `HeroDetailComponent`.
600
+ is bound to the `hero` input property of the `HeroDetailComponent`.
615
601
The `HeroDetailComponent` detects the changed hero and re-sets its form
616
602
with that hero's data values.
617
603
618
604
A "Refresh" button clears the hero list and the current selected hero before refetching the heroes.
619
- The `HeroListComponent` also has an `onSave` method that you'll wire up later in this guide.
620
605
621
606
The remaining `HeroListComponent` and `HeroService` implementation details are not relevant to understanding reactive forms.
622
607
The techniques involved are covered elsewhere in the documentation, including the _Tour of Heroes_
@@ -684,7 +669,7 @@ a#form-array
684
669
You need a method to populate (or repopulate) the _secretLairs_ with actual hero addresses whenever
685
670
the parent `HeroListComponent` sets the `HeroListComponent.hero` input property to a new `Hero`.
686
671
687
- The following method replaces the _secretLairs_ `FormArray` with a new `FormArray`,
672
+ The following `setAddresses` method replaces the _secretLairs_ `FormArray` with a new `FormArray`,
688
673
initialized by an array of hero address `FormGroups`.
689
674
+ makeExample('reactive-forms/ts/app/hero-detail-7.component.ts' , 'set-addresses' )( format ="." )
690
675
@@ -780,9 +765,9 @@ a#observe-control
780
765
that raises a change event.
781
766
782
767
These are properties, such as `valueChanges`, that return an RxJS `Observable`.
783
- You don't need to know much about RxJS `Observable` to watch for a form control value change .
768
+ You don't need to know much about RxJS `Observable` to monitor form control values .
784
769
785
- Add the following method to watch for changes to the value of the _name_ `FormControl`.
770
+ Add the following method to log changes to the value of the _name_ `FormControl`.
786
771
+ makeExample('reactive-forms/ts/app/hero-detail.component.ts' , 'log-name-change' ,'app/hero-detail.component.ts (logNameChange)' )( format ="." )
787
772
788
773
:marked
@@ -819,53 +804,33 @@ figure.image-display
819
804
820
805
:marked
821
806
### Save
822
- In this sample application, the `HeroDetailComponent` cannot perform the save operation.
823
- The component delegates that chore to its parent `HeroListComponent`.
824
-
825
- When the user clicks the _Save_ button, the `HeroDetailComponent` raises a save event on its `save` output property.
826
- The parent `HeroListComponent`, which binds to that property, responds to the event and processes the event payload.
827
- + makeExample('reactive-forms/ts/app/hero-list.component.html' , 'hero-binding' ,'app/hero-list.component.html (save event binding)' )( format ="." )
828
- :marked
829
- The save event payload _could_ have any shape but often it's in the shape of the data model and that is the case in this example.
830
- + makeExample('reactive-forms/ts/app/hero-detail.component.ts' , 'save-emitter' )( format ="." )
831
-
832
- :marked
833
- The `HeroDetailComponent` save method (`onSubmit`) constructs a `saveHero`
834
- and emits a save message, with `saveHero` as the payload, for the `HeroListComponent` to process.
835
-
807
+ In this sample application, when the user submits the form,
808
+ the `HeroDetailComponent` will pass an instance of the hero _data model_
809
+ to a save method on the injected `HeroService`.
836
810
+ makeExample('reactive-forms/ts/app/hero-detail.component.ts' , 'on-submit' ,'app/hero-detail.component.ts (onSubmit)' )( format ="." )
837
811
:marked
838
- In the reactive paradigm, the _data model_ is immutable so the save method can't update the component's `hero`
839
- and send that as the save event payload.
840
-
841
- Instead, it must prepare `saveHero` whose values derive from a combination of original hero values (the `hero.id`)
842
- and deep copies of the potentially-changed form model values.
843
-
844
- ## Ward, stop lecturing :D
812
+ This original `hero` had the pre-save values. The user's changes are still in the _form model_.
813
+ So you create a new `hero` from a combination of original hero values (the `hero.id`)
814
+ and deep copies of the changed form model values, using the `prepareSaveHero` helper.
845
815
846
816
+ makeExample('reactive-forms/ts/app/hero-detail.component.ts' , 'prepare-save-hero' ,'app/hero-detail.component.ts (prepareSaveHero)' )( format ="." )
847
817
848
- :marked
849
- In the reactive paradigm, the form model and its object contents must not leak out of the component.
850
- The `saveHero` in the event payload must be completely disconnected from the form model.
851
- Ideally, it is as immutable as the component's source `hero`.
852
-
853
- If you assign the `formModel.secretLairs` to `saveHero.addresses` (see line commented out),
854
- the addresses in the `saveHero.addresses` array will be the same objects
855
- as the lairs in the `formModel.secretLairs`.
856
- A user's subsequent changes to a lair street will mutate an address street in the `saveHero`.
857
- The `prepareSaveHero` method makes copies of the form model's `secretLairs` objects so that can't happen.
818
+ .l-sub-section
819
+ :marked
820
+ **Address deep copy**
858
821
859
- Keeping data immutable takes effort and vigilance.
860
- Consider using an immutable helper library such as
861
- <a href="https://facebook.github.io/immutable-js/" target="_blank" title="Immutable">Immutable.js</a>.
822
+ Had you assigned the `formModel.secretLairs` to `saveHero.addresses` (see line commented out),
823
+ the addresses in `saveHero.addresses` array would be the same objects
824
+ as the lairs in the `formModel.secretLairs`.
825
+ A user's subsequent changes to a lair street would mutate an address street in the `saveHero`.
826
+
827
+ The `prepareSaveHero` method makes copies of the form model's `secretLairs` objects so that can't happen.
862
828
863
829
:marked
864
830
### Revert (cancel changes)
865
831
The user cancels changes and reverts the form to the original state by pressing the _Revert_ button.
866
832
867
- Reverting is easy. Simply re-execute the `ngOnChanges` method that built the _form model_ from the current `hero` _data model_.
868
- The immutability rule guarantees that the `hero` hasn't changed.
833
+ Reverting is easy. Simply re-execute the `ngOnChanges` method that built the _form model_ from the original, unchanged `hero` _data model_.
869
834
+ makeExample('reactive-forms/ts/app/hero-detail.component.ts' , 'revert' ,'app/hero-detail.component.ts (revert)' )( format ="." )
870
835
871
836
:marked
@@ -894,7 +859,7 @@ figure.image-display
894
859
- Inspecting `FormControl` properties.
895
860
- Setting data with `patchValue` and `setValue`.
896
861
- Adding groups dynamically with `FormArray`.
897
- - Watching for changes to the value of a `FormControl`.
862
+ - Observing changes to the value of a `FormControl`.
898
863
- Saving form changes.
899
864
900
865
a#source-code
0 commit comments