🤘 Angularの新しいロゴとベータ版ドキュメント が公開されました!

Angular v17では Control Flow と Deferred Loading という新機能がリリースされます。

記事執筆時点(2023/11)のステータスはRFC(Request For Comments)で、v17でもDevelopers Previewとしてのリリースが予測されます。今後シンタックスが変更になる可能性があるため、当記事内のサンプルコードはざっくり概要を掴むものとして捉えていただければと思います。

どんな機能?

*ngIf や *ngFor などの構造化ディレクティブの代替となるテンプレートの新しいシンタックスです。

この記事と同日公開のangular.devにて、 Built-in control flow セクションで詳しく説明されています。またRFCのサマリが [Summary] Control Flow & Deferred Loading に記載されています。

2023/11/07 12:15 angular.devのURLを教えていただいたので追記しました。

Control Flow は @if @for @switch を提供します。

<!-- 構造化ディレクティブ -->
<div *ngIf="flag; else empty">
  ...
</div>

<ng-template #empty>
  Nothing to see here...
</ng-template>
<!-- Control Flow -->
@if (flag) {
  ...
} @else {
  Nothing to see here...
}

Deferred Loading は遅延ロードのための @defer を提供します。

@defer (on viewport) {
  ...
} @placeholder {
  ...
} @loading {
  Now loading...
}

Control Flow、Deferred Loadingともに @ をプレフィクスとしたブロック構文を採用しています。{# も候補に上がっていましたが、コミュニティのサーベイの結果 @ に人気が集まったということでした。

Meet Angular’s New Control Flow | Angular Blog

メールアドレスなど既存のテンプレートに @ が存在する場合は ng update によってエスケープされるそうです。

以降は @angular/core 17.0.0-rc.1 で動かしたサンプルコードを紹介します。

@if

@if (isSignIn) {
  ...
} @else {
  ...
  <button (click)="signIn()">Sign In</button>
}
export class LoginComponent {
  isSignIn = false;

  signIn() {
    this.isSignIn = true;
  }
}

GitHubで全文を見る

@if をはじめとしてブロック構文は独立した行に記述します。構造化ディレクティブ *ngIf はDOMのAttributeが多くなるにつれ見通しが悪くなるため、個人的にとても嬉しい記述方法です。

@for

@for (post of posts; track $index) {
  <li>
    <span>{{ post.title }}</span>
  </li>
} @empty {
  ...
}
export class BlogComponent {
  posts: Post[] = [
    { title: "A" },
    { title: "B" },
    { title: "C" },
  ];
}

GitHubで全文を見る

@empty が記述できるのが嬉しいですね。*ngFor の trackBy 相当の track は必須指定となるようです。

@switch

@switch (flag) {
  @case ('A') {
    <p></p>
  }
  @case ('B') {
    <p></p>
  }
  @case ('C') {
    <p></p>
  }
  @default { ... }
}

GitHubで全文を見る

こちらも *ngSwitch に比べてテンプレートがだいぶすっきりしそうです。

@defer

@defer (when loaded) {
  C
} @loading {
  B
} @placeholder {
  A
}

GitHubで全文を見る

@defer は遅延ロードの状態変化によりコンテンツを出し分けます。それぞれのブロックは以下のタイミングで評価され、画面にはA、B、Cの順でコンテンツがレンダリングされます。

  • @placeholder 最初のレンダリング
  • @loading 依存解決の間
  • @defer 条件にマッチした時

@defer に続くかっこの部分は (on {トリガ}) (when {変数}) のような条件式が入ります。

たとえば次のコードは、ボタン「start」をクリックした時にコンテンツを表示します。

<button #start></button>

@defer (on interaction(start)) {
  ...
}

次のコードは変数「loaded」が真と評価された時にコンテンツを表示します。

@defer (when loaded) {
  ...
}

組み合わせると、初期値「A」、ボタンをクリックすると「B」、loadedが真になると「C」のようにレンダリングされます。

@defer (on interaction(start)) {
  @defer (when loaded) {
    C
  } @placeholder {
    B
  }
} @placeholder {
  A
}

DOMに対する変更

ブロック構文は評価のタイミングでDOM要素を再構築します。この挙動は構造化ディレクティブと変わりません。

考察

Angularでは、Signalsに続きControl Flow & Deferred Loadingの導入と、DOM再構築をシンプル化しパフォーマンスを向上させる機能リリースが続いています。

今までネストした *ngIf が見づらく専用フラグを用意することもありましたが、独立した行のブロック構文の見やすさとともに徐々に減っていくのではないかと思います。これは単に「見やすい」だけでなく、テンプレートのことはテンプレートに任せるという関心の分離にも貢献します。

また、パフォーマンス改善はビルドやデプロイした成果物の実行スピードを上げるだけでなく、開発者体験も向上させます。思えばAngular v2がリリースされた頃は、コンポーネントを追加するたびにビルドがおかしくなるので ng serve をやり直していました。v17リリースを見据えた今ではファイルシステムの変更検知とHMRがしっかり連携しています。コードの変更による再レンダリングのタイムラグが短くなるほど、開発者体験は良いものになると実感しています。

安定版リリースのしかるべきタイミングでAngular wayに乗っかっていくことで、私達の開発者体験はさらにアップするのではないでしょうか。

カルテット開発部について

カルテット開発部は、インターネット広告専門の広告代理店内の開発部門です。
少人数の部署ですが、広告運用を効率化する Lisket とウェブサイト運営に欠かせないGoogleAnalyticsのレポートをエクセル形式で出力できる 無限GAレポートメーカー という2つのWebサービスを開発〜運営まで行っています。既存システムの保守だけでなく社内システムや新規事業などの新規プロダクトの開発も進行中で、新規プロダクトではスクラム開発をしています。

バックエンドエンジニア絶賛募集中です!少しでも興味を持っていただいた方はぜひ募集要項をごらんください。