@@ -21,6 +21,7 @@ block includes
21
21
* [Property binding](#property-binding)
22
22
* [Attribute, class, and style bindings](#other-bindings)
23
23
* [Event binding](#event-binding)
24
+ * [Two-way data binding](#two-way)
24
25
* [Two-way data binding with `NgModel`](#ngModel)
25
26
* [Built-in directives](#directives)
26
27
* [NgClass](#ngClass)
@@ -841,19 +842,64 @@ block style-property-name-dart-diff
841
842
and the outer `<div>`, causing a double save.
842
843
+makeExample('template-syntax/ts/app/app.component.html', 'event-binding-propagation')(format=".")
843
844
845
+ #two-way
846
+ .l-main-section
847
+ :marked
848
+ ## Two-way binding
849
+ We often want to both display a data property and update that property when the user makes changes.
850
+
851
+ On the element side that takes a combination of setting a specific element property
852
+ and listening for an element change event.
853
+
854
+ Angular offers a special _two-way data binding_ syntax for this purpose, **`[(x)]`**.
855
+ The `[(x)]` syntax combines the brackets
856
+ of _Property Binding_, `[x]`, with the parentheses of _Event Binding_, `(x)`.
857
+ .callout.is-important
858
+ header [( )] = banana in a box
859
+ :marked
860
+ Visualize a *banana in a box* to remember that the parentheses go _inside_ the brackets.
861
+ :marked
862
+ The `[(x)]` syntax is easy to demonstrate when the element has a settable property called `x`
863
+ and a corresponding event named `xChange`.
864
+ Here's a `SizerComponent` that fits the pattern.
865
+ It has a `size` value property and a companion `sizeChange` event:
866
+ + makeExample('template-syntax/ts/app/sizer.component.ts' , null , 'app/sizer.component.ts' )
867
+ :marked
868
+ The initial `size` is an input value from a property binding.
869
+ Clicking the buttons increases or decreases the `size`, within min/max values constraints,
870
+ and then raises (_emits_) the `sizeChange` event with the adjusted size.
871
+
872
+ Here's an example in which the `AppComponent.fontSize` is two-way bound to the `SizerComponent`:
873
+ + makeExample('template-syntax/ts/app/app.component.html' , 'two-way-1' )( format ="." )
874
+ :marked
875
+ The `AppComponent.fontSize` establishes the initial `SizerComponent.size` value.
876
+ Clicking the buttons updates the `AppComponent.fontSize` via the two-way binding.
877
+ The revised `AppComponent.fontSize` value flows through to the _style_ binding, making the displayed text bigger or smaller.
878
+ Try it in the <live-example>live example</live-example>.
879
+
880
+ The two-way binding syntax is really just syntactic sugar for a _property_ binding and an _event_ binding.
881
+ Angular _de-sugars_ that `SizerComponent` binding into this:
882
+ + makeExample('template-syntax/ts/app/app.component.html' , 'two-way-2' )( format ="." )
883
+ :marked
884
+ The `$event` variable contains the payload of the `SizerComponent.sizeChange` event.
885
+ Angular assigns the `$event` value to the `AppComponent.fontSize` when the user clicks the buttons.
886
+
887
+ Clearly the two-way binding syntax is a great convenience compared to separate property and event bindings.
888
+
889
+ We'd like to use two-way binding with HTML form elements like `<input>` and `<select>`.
890
+ Sadly, no native HTML element follows the `x` value and `xChange` event pattern.
844
891
892
+ Fortunately, the Angular [_NgModel_](#ngModel) directive is a bridge that enables two-way binding to form elements.
893
+
894
+ a#ngModel
845
895
.l-main-section
846
896
:marked
847
- <a id="ngModel"></a>
848
897
## Two-way binding with NgModel
849
898
When developing data entry forms, we often want to both display a data property and update that property when the user makes changes.
850
899
851
- The `[(ngModel)]` two -way data binding syntax makes that easy. Here's an example:
900
+ Two -way data binding with the `NgModel` directive makes that easy. Here's an example:
852
901
+ makeExample('template-syntax/ts/app/app.component.html' , 'NgModel-1' )( format ="." )
853
- .callout.is-important
854
- header [()] = banana in a box
855
- :marked
856
- To remember that the parentheses go inside the brackets, visualize a *banana in a box*.
902
+
857
903
858
904
+ ifDocsFor('ts|js' )
859
905
.callout.is-important
@@ -863,54 +909,48 @@ block style-property-name-dart-diff
863
909
we must import the `FormsModule` and add it to the Angular module's `imports` list.
864
910
Learn more about the `FormsModule` and `ngModel` in the
865
911
[Forms](../guide/forms.html#ngModel) chapter.
866
-
912
+ :marked
913
+ Here's how to import the `FormsModule` to make `[(ngModel)]` available.
867
914
+ makeExample('template-syntax/ts/app/app.module.1.ts' , '' , 'app.module.ts (FormsModule import)' )
868
915
869
916
:marked
870
- There’s a story behind this construction, a story that builds on the property and event binding techniques we learned previously.
871
-
872
917
### Inside `[(ngModel)]`
873
- We could have achieved the same result with separate bindings to
918
+ Looking back at the `firstName` binding, it's important to note that
919
+ we could have achieved the same result with separate bindings to
874
920
the `<input>` element's `value` property and `input` event.
875
921
+ makeExample('template-syntax/ts/app/app.component.html' , 'without-NgModel' )( format ="." )
876
922
:marked
877
- That’s cumbersome. Who can remember which element property to set and what event reports user changes?
923
+ That’s cumbersome. Who can remember which element property to set and which element event emits user changes?
878
924
How do we extract the currently displayed text from the input box so we can update the data property?
879
925
Who wants to look that up each time?
880
926
881
927
That `ngModel` directive hides these onerous details behind its own `ngModel` input and `ngModelChange` output properties.
882
928
+ makeExample('template-syntax/ts/app/app.component.html' , 'NgModel-3' )( format ="." )
883
929
.l-sub-section
884
930
:marked
885
- The `ngModel` input property sets the element's value property and the `ngModelChange` output property
931
+ The `ngModel` data property sets the element's value property and the `ngModelChange` event property
886
932
listens for changes to the element's value.
887
- The details are specific to each kind of element and therefore the `NgModel` directive only works for elements,
933
+
934
+ The details are specific to each kind of element and therefore the `NgModel` directive only works for specific form elements,
888
935
such as the input text box, that are supported by a [ControlValueAccessor](../api/forms/index/ControlValueAccessor-interface.html).
889
- We can't apply `[(ngModel)]` to our custom components until we write a suitable *value accessor*,
936
+
937
+ We can't apply `[(ngModel)]` to a custom component until we write a suitable *value accessor*,
890
938
a technique that is beyond the scope of this chapter.
939
+ That's something we might want to do for an Angular component or a WebComponent whose API we can't control.
940
+
941
+ It's completely unnecessary for an Angular component that we _do_ control ... because we can name the value and event properties
942
+ to suit Angular's basic [two-way binding syntax](#two-way) and skip `NgModel` altogether.
891
943
892
944
:marked
893
- Separate `ngModel` bindings is an improvement. We can do better.
945
+ Separate `ngModel` bindings is an improvement over binding to the element's native properties . We can do better.
894
946
895
947
We shouldn't have to mention the data property twice. Angular should be able to capture the component’s data property and set it
896
- with a single declaration — which it can with the `[( )]` syntax:
948
+ with a single declaration — which it can with the `[(ngModel )]` syntax:
897
949
+ makeExample('template-syntax/ts/app/app.component.html' , 'NgModel-1' )( format ="." )
898
-
899
- .l-sub-section
900
- :marked
901
- `[(ngModel)]` is a specific example of a more general pattern in which Angular "de-sugars" the `[(x)]` syntax
902
- into an `x` input property for property binding and an `xChange` output property for event binding.
903
- Angular constructs the event property binding's template statement by appending `=$event`
904
- to the literal string of the template expression.
905
-
906
- > <span style="font-family:courier">[(_x_)]="_e_" <==> [_x_]="_e_" (<i>x</i>Change)="_e_=$event"</span>
907
-
908
- We can write a two-way binding directive of our own to exploit this behavior.
909
-
910
950
:marked
911
951
Is `[(ngModel)]` all we need? Is there ever a reason to fall back to its expanded form?
912
952
913
- The `[( )]` syntax can only _set_ a data-bound property.
953
+ The `[(ngModel )]` syntax can only _set_ a data-bound property.
914
954
If we need to do something more or something different, we need to write the expanded form ourselves.
915
955
916
956
Let's try something silly like forcing the input value to uppercase:
0 commit comments