Edited at

Angularの「?.」でつまづいた


事象

AngularのTemplate Syntaxである「?.」(Safe Navigation Operator)で思わぬ障害を作りこんでしまいました。

Angularのテンプレートにて

<span *ngIf="obj.getData()?.indexOf(':') >= 0">

テスト
</span>

というコードを書きました。

コードの目的は、getData()がnullでない場合、:が文字列中に含まれるなら1「テスト」という文字列を出力し、その他の場合は、何も表示しないようにすることです。

getData()は戻り値として、文字列を返します。ただし、nullを含むことがあります。

そこで、AngularのSafe Navigation Operator(getData()の直後の「?.」)を用いて、



  • getData()がnullでないなら以降の式を実行

  • nullなら実行しない(ngIf全体でfalse)

となるようコーディングしたつもりでした。

しかし、getData()の戻りがnullの場合でも、「テスト」が表示されてしまいました。


原因

Safe Navigation Operatorの仕様を勘違いしていたことです。

Safe Navigation Operatorはnull(またはundefined)の場合その後の式を評価しないのではなく、続くプロパティにアクセスしないことです2

よって、getData()がnullである場合、上記のコードは以下のように評価されます。

<span *ngIf="obj.getData() >= 0">

テスト
</span>

JavaScriptにおいて、>=で比較する際に、0とnullは同じものとして評価されます。

よって上記の式の結果はtrueとなり、「テスト」の文字列が表示されてしまいました。


解消法

事前にnullの判定を入れました。

<ng-container *ngIf="obj.getData()">

<span *ngIf="obj.getData().indexOf(':') >= 0">
テスト
</span>
</ng-container>





  1. そもそも「文字列中に含まれるかどうか」ならString.prototype.includes()を使いたいところなのですが、IE11対応がシステム要件に含まれるため、使えませんでした。 



  2. Angualrのドキュメントには「The expression bails out when it hits the first null value. 」とあります。