parent
0e63b1f39b
commit
4097c67b59
|
@ -34,7 +34,7 @@
|
||||||
<a href="#local-vars">Template local variables</a><br>
|
<a href="#local-vars">Template local variables</a><br>
|
||||||
<a href="#inputs-and-outputs">Inputs and outputs</a><br>
|
<a href="#inputs-and-outputs">Inputs and outputs</a><br>
|
||||||
<a href="#pipes">Pipes</a><br>
|
<a href="#pipes">Pipes</a><br>
|
||||||
<a href="#elvis">Elvis <i>?.</i></a><br>
|
<a href="#safe-navigation-operator">Safe navigation operator <i>?.</i></a><br>
|
||||||
<!--<a href="#enums">Enums</a><br>-->
|
<!--<a href="#enums">Enums</a><br>-->
|
||||||
|
|
||||||
<!-- Interpolation and expressions -->
|
<!-- Interpolation and expressions -->
|
||||||
|
@ -738,25 +738,25 @@ bindon-ngModel
|
||||||
|
|
||||||
<a class="to-toc" href="#toc">top</a>
|
<a class="to-toc" href="#toc">top</a>
|
||||||
|
|
||||||
<!-- Null values and the Elvis operator -->
|
<!-- Null values and the safe navigation operator -->
|
||||||
<hr><h2 id="elvis">Elvis <i>?.</i></h2>
|
<hr><h2 id="safe-navigation-operator">Safe navigation operator <i>?.</i></h2>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<!-- #docregion elvis-1 -->
|
<!-- #docregion safe-1 -->
|
||||||
The title is {{ title }}
|
The title is {{ title }}
|
||||||
<!-- #enddocregion elvis-1 -->
|
<!-- #enddocregion safe-1 -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<!-- #docregion elvis-2 -->
|
<!-- #docregion safe-2 -->
|
||||||
The current hero's name is {{currentHero?.firstName}}
|
The current hero's name is {{currentHero?.firstName}}
|
||||||
<!-- #enddocregion elvis-2 -->
|
<!-- #enddocregion safe-2 -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<!-- #docregion elvis-3 -->
|
<!-- #docregion safe-3 -->
|
||||||
The current hero's name is {{currentHero.firstName}}
|
The current hero's name is {{currentHero.firstName}}
|
||||||
<!-- #enddocregion elvis-3 -->
|
<!-- #enddocregion safe-3 -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@ -768,18 +768,18 @@ The null hero's name is {{nullHero.firstName}}
|
||||||
EXCEPTION: The null object does not have a getter 'firstName'.
|
EXCEPTION: The null object does not have a getter 'firstName'.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- #docregion elvis-4 -->
|
<!-- #docregion safe-4 -->
|
||||||
<!--No hero, div not displayed, no error -->
|
<!--No hero, div not displayed, no error -->
|
||||||
<div *ngIf="nullHero != null">The null hero's name is {{nullHero.firstName}}</div>
|
<div *ngIf="nullHero != null">The null hero's name is {{nullHero.firstName}}</div>
|
||||||
<!-- #enddocregion elvis-4 -->
|
<!-- #enddocregion safe-4 -->
|
||||||
|
|
||||||
<!-- skip docregion elvis-5 -->
|
<!-- skip docregion safe-5 -->
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<!-- #docregion elvis-6 -->
|
<!-- #docregion safe-6 -->
|
||||||
<!-- No hero, no problem! -->
|
<!-- No hero, no problem! -->
|
||||||
The null hero's name is {{nullHero?.firstName}}
|
The null hero's name is {{nullHero?.firstName}}
|
||||||
<!-- #enddocregion elvis-6 -->
|
<!-- #enddocregion safe-6 -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
<a href="#local-vars">Template local variables</a><br>
|
<a href="#local-vars">Template local variables</a><br>
|
||||||
<a href="#inputs-and-outputs">Inputs and outputs</a><br>
|
<a href="#inputs-and-outputs">Inputs and outputs</a><br>
|
||||||
<a href="#pipes">Pipes</a><br>
|
<a href="#pipes">Pipes</a><br>
|
||||||
<a href="#elvis">Elvis <i>?.</i></a><br>
|
<a href="#safe-navigation-operator">Safe navigation operator <i>?.</i></a><br>
|
||||||
<a href="#enums">Enums</a><br>
|
<a href="#enums">Enums</a><br>
|
||||||
|
|
||||||
<!-- Interpolation and expressions -->
|
<!-- Interpolation and expressions -->
|
||||||
|
@ -739,25 +739,25 @@ After setClasses(), the classes are "{{classDiv.className}}"
|
||||||
|
|
||||||
<a class="to-toc" href="#toc">top</a>
|
<a class="to-toc" href="#toc">top</a>
|
||||||
|
|
||||||
<!-- Null values and the Elvis operator -->
|
<!-- Null values and the safe navigation operator -->
|
||||||
<hr><h2 id="elvis">Elvis <i>?.</i></h2>
|
<hr><h2 id="safe-navigation-operator">Safe navigation operator <i>?.</i></h2>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<!-- #docregion elvis-1 -->
|
<!-- #docregion safe-1 -->
|
||||||
The title is {{ title }}
|
The title is {{ title }}
|
||||||
<!-- #enddocregion elvis-1 -->
|
<!-- #enddocregion safe-1 -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<!-- #docregion elvis-2 -->
|
<!-- #docregion safe-2 -->
|
||||||
The current hero's name is {{currentHero?.firstName}}
|
The current hero's name is {{currentHero?.firstName}}
|
||||||
<!-- #enddocregion elvis-2 -->
|
<!-- #enddocregion safe-2 -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<!-- #docregion elvis-3 -->
|
<!-- #docregion safe-3 -->
|
||||||
The current hero's name is {{currentHero.firstName}}
|
The current hero's name is {{currentHero.firstName}}
|
||||||
<!-- #enddocregion elvis-3 -->
|
<!-- #enddocregion safe-3 -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@ -768,22 +768,22 @@ See console log
|
||||||
TypeError: Cannot read property 'firstName' of null in [null]
|
TypeError: Cannot read property 'firstName' of null in [null]
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- #docregion elvis-4 -->
|
<!-- #docregion safe-4 -->
|
||||||
<!--No hero, div not displayed, no error -->
|
<!--No hero, div not displayed, no error -->
|
||||||
<div *ngIf="nullHero">The null hero's name is {{nullHero.firstName}}</div>
|
<div *ngIf="nullHero">The null hero's name is {{nullHero.firstName}}</div>
|
||||||
<!-- #enddocregion elvis-4 -->
|
<!-- #enddocregion safe-4 -->
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<!-- #docregion elvis-5 -->
|
<!-- #docregion safe-5 -->
|
||||||
The null hero's name is {{nullHero && nullHero.firstName}}
|
The null hero's name is {{nullHero && nullHero.firstName}}
|
||||||
<!-- #enddocregion elvis-5 -->
|
<!-- #enddocregion safe-5 -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<!-- #docregion elvis-6 -->
|
<!-- #docregion safe-6 -->
|
||||||
<!-- No hero, no problem! -->
|
<!-- No hero, no problem! -->
|
||||||
The null hero's name is {{nullHero?.firstName}}
|
The null hero's name is {{nullHero?.firstName}}
|
||||||
<!-- #enddocregion elvis-6 -->
|
<!-- #enddocregion safe-6 -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -366,11 +366,11 @@ table
|
||||||
:marked
|
:marked
|
||||||
## Template expression operators
|
## Template expression operators
|
||||||
The template expression language employs a subset of Dart syntax supplemented with a few special operators
|
The template expression language employs a subset of Dart syntax supplemented with a few special operators
|
||||||
for specific scenarios. We'll cover two of these operators: _pipe_ and _Elvis_.
|
for specific scenarios. We'll cover two of these operators: _pipe_ and _safe navigation operator_.
|
||||||
.callout.is-helpful
|
.callout.is-helpful
|
||||||
header Dart difference: ?. is a Dart operator
|
header Dart difference: ?. is a Dart operator
|
||||||
:marked
|
:marked
|
||||||
The Elvis operator (`?.`) is part of the Dart language.
|
The safe navigation operator (`?.`) is part of the Dart language.
|
||||||
It's considered a template expression operator because
|
It's considered a template expression operator because
|
||||||
Angular 2 supports `?.` even in TypeScript and JavaScript apps.
|
Angular 2 supports `?.` even in TypeScript and JavaScript apps.
|
||||||
+includeShared('{ts}', 'expression-operators-pipe-1')
|
+includeShared('{ts}', 'expression-operators-pipe-1')
|
||||||
|
@ -383,28 +383,28 @@ table
|
||||||
NOTE: Intentionally omit discussion of the json pipe.
|
NOTE: Intentionally omit discussion of the json pipe.
|
||||||
+includeShared('{ts}', 'expression-operators-pipe-4')
|
+includeShared('{ts}', 'expression-operators-pipe-4')
|
||||||
+makeExample('template-syntax/dart/lib/app_component.html', 'pipes-json')(format=".")
|
+makeExample('template-syntax/dart/lib/app_component.html', 'pipes-json')(format=".")
|
||||||
+includeShared('{ts}', 'expression-operators-elvis-1')
|
+includeShared('{ts}', 'expression-operators-safe-1')
|
||||||
+makeExample('template-syntax/dart/lib/app_component.html', 'elvis-2')(format=".")
|
+makeExample('template-syntax/dart/lib/app_component.html', 'safe-2')(format=".")
|
||||||
+includeShared('{ts}', 'expression-operators-elvis-2')
|
+includeShared('{ts}', 'expression-operators-safe-2')
|
||||||
+makeExample('template-syntax/dart/lib/app_component.html', 'elvis-1')(format=".")
|
+makeExample('template-syntax/dart/lib/app_component.html', 'safe-1')(format=".")
|
||||||
+includeShared('{ts}', 'expression-operators-elvis-3')
|
+includeShared('{ts}', 'expression-operators-safe-3')
|
||||||
// +includeShared('{ts}', 'expression-operators-elvis-4')
|
// +includeShared('{ts}', 'expression-operators-safe-4')
|
||||||
:marked
|
:marked
|
||||||
Dart throws an exception, and so does Angular:
|
Dart throws an exception, and so does Angular:
|
||||||
code-example(format="" language="html").
|
code-example(format="" language="html").
|
||||||
EXCEPTION: The null object does not have a getter 'firstName'.
|
EXCEPTION: The null object does not have a getter 'firstName'.
|
||||||
+includeShared('{ts}', 'expression-operators-elvis-5')
|
+includeShared('{ts}', 'expression-operators-safe-5')
|
||||||
+makeExample('template-syntax/dart/lib/app_component.html', 'elvis-4')(format=".")
|
+makeExample('template-syntax/dart/lib/app_component.html', 'safe-4')(format=".")
|
||||||
|
|
||||||
//
|
//
|
||||||
NOTE: Intentionally skip ugly null checking you wouldn't do in Dart.
|
NOTE: Intentionally skip ugly null checking you wouldn't do in Dart.
|
||||||
That means skipping the shared sections 'expression-operators-elvis-6' & 7,
|
That means skipping the shared sections 'expression-operators-safe-6' & 7,
|
||||||
plus the example 'elvis-5'.
|
plus the example 'safe-5'.
|
||||||
:marked
|
:marked
|
||||||
This approach has merit but can be cumbersome, especially if the property path is long.
|
This approach has merit but can be cumbersome, especially if the property path is long.
|
||||||
Imagine guarding against a null somewhere in a long property path such as `a.b.c.d`.
|
Imagine guarding against a null somewhere in a long property path such as `a.b.c.d`.
|
||||||
+includeShared('{ts}', 'expression-operators-elvis-8')
|
+includeShared('{ts}', 'expression-operators-safe-8')
|
||||||
+makeExample('template-syntax/dart/lib/app_component.html', 'elvis-6')(format=".")
|
+makeExample('template-syntax/dart/lib/app_component.html', 'safe-6')(format=".")
|
||||||
+includeShared('{ts}', 'expression-operators-elvis-9')
|
+includeShared('{ts}', 'expression-operators-safe-9')
|
||||||
|
|
||||||
+includeShared('{ts}', 'summary')
|
+includeShared('{ts}', 'summary')
|
||||||
|
|
|
@ -28,7 +28,7 @@ include ../_util-fns
|
||||||
* [Input and output properties](#inputs-outputs)
|
* [Input and output properties](#inputs-outputs)
|
||||||
* [Template expression operators](#expression-operators)
|
* [Template expression operators](#expression-operators)
|
||||||
* [pipe](#pipe)
|
* [pipe](#pipe)
|
||||||
* ["elvis" (?.)](#elvis)
|
* ["safe navigation operator" (?.)](#safe-navigation-operator)
|
||||||
// #enddocregion intro
|
// #enddocregion intro
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
|
@ -1576,7 +1576,7 @@ figure.image-display
|
||||||
:marked
|
:marked
|
||||||
## Template expression operators
|
## Template expression operators
|
||||||
The template expression language employs a subset of JavaScript syntax supplemented with a few special operators
|
The template expression language employs a subset of JavaScript syntax supplemented with a few special operators
|
||||||
for specific scenarios. We'll cover two of these operators: _pipe_ and _Elvis_.
|
for specific scenarios. We'll cover two of these operators: _pipe_ and _safe navigation operator_.
|
||||||
// #enddocregion expression-operators
|
// #enddocregion expression-operators
|
||||||
|
|
||||||
// #docregion expression-operators-pipe-1
|
// #docregion expression-operators-pipe-1
|
||||||
|
@ -1608,23 +1608,23 @@ figure.image-display
|
||||||
// #enddocregion expression-operators-pipe-4
|
// #enddocregion expression-operators-pipe-4
|
||||||
+makeExample('template-syntax/ts/app/app.component.html', 'pipes-json')(format=".")
|
+makeExample('template-syntax/ts/app/app.component.html', 'pipes-json')(format=".")
|
||||||
|
|
||||||
// #docregion expression-operators-elvis-1
|
// #docregion expression-operators-safe-1
|
||||||
:marked
|
:marked
|
||||||
<a id="elvis"></a>
|
<a id="safe-navigation-operator"></a>
|
||||||
### The Elvis operator ( ?. ) and null property paths
|
### The safe navigation operator ( ?. ) and null property paths
|
||||||
|
|
||||||
The Angular **Elvis operator (`?.`)** — perhaps better described as the "safe navigation operator" — is a fluent and convenient way to guard against null and undefined values in property paths.
|
The Angular **safe navigation operator (`?.`)** is a fluent and convenient way to guard against null and undefined values in property paths.
|
||||||
Here it is, protecting against a view render failure if the `currentHero` is null.
|
Here it is, protecting against a view render failure if the `currentHero` is null.
|
||||||
// #enddocregion expression-operators-elvis-1
|
// #enddocregion expression-operators-safe-1
|
||||||
+makeExample('template-syntax/ts/app/app.component.html', 'elvis-2')(format=".")
|
+makeExample('template-syntax/ts/app/app.component.html', 'safe-2')(format=".")
|
||||||
// #docregion expression-operators-elvis-2
|
// #docregion expression-operators-safe-2
|
||||||
:marked
|
:marked
|
||||||
Let’s elaborate on the problem and this particular solution.
|
Let’s elaborate on the problem and this particular solution.
|
||||||
|
|
||||||
What happens when the following data bound `title` property is null?
|
What happens when the following data bound `title` property is null?
|
||||||
// #enddocregion expression-operators-elvis-2
|
// #enddocregion expression-operators-safe-2
|
||||||
+makeExample('template-syntax/ts/app/app.component.html', 'elvis-1')(format=".")
|
+makeExample('template-syntax/ts/app/app.component.html', 'safe-1')(format=".")
|
||||||
// #docregion expression-operators-elvis-3
|
// #docregion expression-operators-safe-3
|
||||||
:marked
|
:marked
|
||||||
The view still renders but the displayed value is blank; we see only "The title is" with nothing after it.
|
The view still renders but the displayed value is blank; we see only "The title is" with nothing after it.
|
||||||
That is reasonable behavior. At least the app doesn't crash.
|
That is reasonable behavior. At least the app doesn't crash.
|
||||||
|
@ -1634,14 +1634,14 @@ figure.image-display
|
||||||
|
|
||||||
code-example(format="" language="html").
|
code-example(format="" language="html").
|
||||||
The null hero's name is {{nullHero.firstName}}
|
The null hero's name is {{nullHero.firstName}}
|
||||||
// #enddocregion expression-operators-elvis-3
|
// #enddocregion expression-operators-safe-3
|
||||||
// #docregion expression-operators-elvis-4
|
// #docregion expression-operators-safe-4
|
||||||
:marked
|
:marked
|
||||||
JavaScript throws a null reference error, and so does Angular:
|
JavaScript throws a null reference error, and so does Angular:
|
||||||
code-example(format="" language="html").
|
code-example(format="" language="html").
|
||||||
TypeError: Cannot read property 'firstName' of null in [null]
|
TypeError: Cannot read property 'firstName' of null in [null]
|
||||||
// #enddocregion expression-operators-elvis-4
|
// #enddocregion expression-operators-safe-4
|
||||||
// #docregion expression-operators-elvis-5
|
// #docregion expression-operators-safe-5
|
||||||
:marked
|
:marked
|
||||||
Worse, the *entire view disappears*.
|
Worse, the *entire view disappears*.
|
||||||
|
|
||||||
|
@ -1659,30 +1659,30 @@ code-example(format="" language="html").
|
||||||
Unfortunately, our app crashes when the `currentHero` is null.
|
Unfortunately, our app crashes when the `currentHero` is null.
|
||||||
|
|
||||||
We could code around that problem with [NgIf](#ngIf).
|
We could code around that problem with [NgIf](#ngIf).
|
||||||
// #enddocregion expression-operators-elvis-5
|
// #enddocregion expression-operators-safe-5
|
||||||
+makeExample('template-syntax/ts/app/app.component.html', 'elvis-4')(format=".")
|
+makeExample('template-syntax/ts/app/app.component.html', 'safe-4')(format=".")
|
||||||
// #docregion expression-operators-elvis-6
|
// #docregion expression-operators-safe-6
|
||||||
:marked
|
:marked
|
||||||
Or we could try to chain parts of the property path with `&&`, knowing that the expression bails out
|
Or we could try to chain parts of the property path with `&&`, knowing that the expression bails out
|
||||||
when it encounters the first null.
|
when it encounters the first null.
|
||||||
// #enddocregion expression-operators-elvis-6
|
// #enddocregion expression-operators-safe-6
|
||||||
+makeExample('template-syntax/ts/app/app.component.html', 'elvis-5')(format=".")
|
+makeExample('template-syntax/ts/app/app.component.html', 'safe-5')(format=".")
|
||||||
// #docregion expression-operators-elvis-7
|
// #docregion expression-operators-safe-7
|
||||||
:marked
|
:marked
|
||||||
These approaches have merit but can be cumbersome, especially if the property path is long.
|
These approaches have merit but can be cumbersome, especially if the property path is long.
|
||||||
Imagine guarding against a null somewhere in a long property path such as `a.b.c.d`.
|
Imagine guarding against a null somewhere in a long property path such as `a.b.c.d`.
|
||||||
// #enddocregion expression-operators-elvis-7
|
// #enddocregion expression-operators-safe-7
|
||||||
// #docregion expression-operators-elvis-8
|
// #docregion expression-operators-safe-8
|
||||||
:marked
|
:marked
|
||||||
The Angular Elvis operator (`?.`) is a more fluent and convenient way to guard against nulls in property paths.
|
The Angular safe navigation operator (`?.`) is a more fluent and convenient way to guard against nulls in property paths.
|
||||||
The expression bails out when it hits the first null value.
|
The expression bails out when it hits the first null value.
|
||||||
The display is blank, but the app keeps rolling without errors.
|
The display is blank, but the app keeps rolling without errors.
|
||||||
// #enddocregion expression-operators-elvis-8
|
// #enddocregion expression-operators-safe-8
|
||||||
+makeExample('template-syntax/ts/app/app.component.html', 'elvis-6')(format=".")
|
+makeExample('template-syntax/ts/app/app.component.html', 'safe-6')(format=".")
|
||||||
// #docregion expression-operators-elvis-9
|
// #docregion expression-operators-safe-9
|
||||||
:marked
|
:marked
|
||||||
It works perfectly with long property paths such as `a?.b?.c?.d`.
|
It works perfectly with long property paths such as `a?.b?.c?.d`.
|
||||||
// #enddocregion expression-operators-elvis-9
|
// #enddocregion expression-operators-safe-9
|
||||||
|
|
||||||
// #docregion summary
|
// #docregion summary
|
||||||
.l-main-section
|
.l-main-section
|
||||||
|
|
|
@ -1655,7 +1655,7 @@ code-example(format="").
|
||||||
a property expression, as opposed to a literal string.
|
a property expression, as opposed to a literal string.
|
||||||
* We've replaced `ng-repeat`s with `*ngFor`s.
|
* We've replaced `ng-repeat`s with `*ngFor`s.
|
||||||
* We've replaced `ng-click` with an event binding for the standard `click`.
|
* We've replaced `ng-click` with an event binding for the standard `click`.
|
||||||
* In all references to `phone`, we're using the elvis operator `?.` for
|
* In all references to `phone`, we're using the safe navigation operator `?.` for
|
||||||
safe property navigation. We need it because when the component first loads,
|
safe property navigation. We need it because when the component first loads,
|
||||||
we don't have `phone` yet and the expressions will refer to a non-existing
|
we don't have `phone` yet and the expressions will refer to a non-existing
|
||||||
value. Unlike in Angular 1, Angular 2 expressions do not fail silently when
|
value. Unlike in Angular 1, Angular 2 expressions do not fail silently when
|
||||||
|
|
Loading…
Reference in New Issue