4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-01-24

事象

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. 」とあります。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?