8
2

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 1 year has passed since last update.

Angularの新しい未来:Signalsを実際に使ってみよう

Posted at

ハイサイ!オースティンやいびーん。んな、がんじゅー やいびーみ?

概要

Angular 16のリリースに含まれる予定の新原始型Signalを使っていくつかの具体的な使用例を紹介します!

Signalとは?

Angularのフレームワークが変更をパフォーマンスよく検知するための新しいReactive Primitiveです。

SolidJSのsignalと概念が一緒で、非常に単純です。Signalには以下の二つの特徴があります。

  1. 値を保持している
  2. 値が変わった時に信号を出す能力を持っている

上記の二つの特徴をAngularで使うと、同期的な処理を簡単にテンプレートに反映できます。

SignalとAngularの変更について詳しくは以下の記事をご覧ください。

試験的にSignal APIを使う方法

Signalを今でも使ってみたい!という方、実はもうすでにPOCとしてAngularチームが実装して出してくれています!

使うためには@angular/cli@16.0.0を使う必要があります。

まだ試験的にバージョンがよく変わるので、npmで今現在の最新のバージョンを確認してください。

上記のnpmページから最新のバージョンを取得したら以下のコマンドに代入して実行すればAngular 16のプロジェクトを作ることができます。

npx -p @angular/cli@16.0.0-next.6 ng new signals-play

そうするとng serveを実行してAngularのアプリが起動するか確認してください。
Screenshot 2023-04-03 at 8.59.23.png

Signalを部品とテンプレートで使う

app.component.tssignalをインポートしてみましょう。

import { Component, signal } from '@angular/core';

補足説明が書いてありますが、@developerPreviewになっています。

Screenshot 2023-04-03 at 9.01.28.png

以下のように初期値を代入すると型を推測してくれます。

import { Component, signal } from '@angular/core';

@Component({
...
})
export class AppComponent {
  clickCount = signal(0);

  addClick() {
    this.clickCount.update((clicks) => clicks + 1);
    // OR
    this.clickCount.set(this.clickCount() + 1);
  }
}

テンプレートで以下のように現在の値を表示できます。テンプレートではclickCountを関数として呼び、現在の値を取得します。

app.component.html
<h1>Count: </h1>
<p>{{ this.clickCount() }}</p>

<button (click)="this.addClick()" type="button">Add 2</button>

「テンプレートで関数を呼ぶのは危険」と経験上ご存知の方もいらっしゃるかと思いますが、Signalは副作用を起こさないので心配無用です。

以下のような結果になります。

ezgif.com-video-to-gif.gif

非常に簡単です。

computedの使い方

Signalは上記の説明の通り、値を持ってそれが変わった時にその変化を知らせるという二つの役割しかないのです。

ただ、同期処理の多くは、値と値を使って何かを計算することがほとんどです。

signal同士を使って計算する時は、computedという関数を使います!

以下、ページボタンの例で紹介します。

app.component.ts

import { Component, signal, computed } from '@angular/core';

@Component({
...
})
export class AppComponent {
  private itemCount = signal(124);
  private itemsPerPage = signal(10);
  currentPage = signal(0);
  pageCount = computed(() => Math.ceil(this.itemCount() / this.itemsPerPage()));
  canGoForward = computed(() => this.currentPage() < this.pageCount());
  canGoBack = computed(() => this.currentPage() !== 0);

  goForward() {
    if (!this.canGoForward()) return;
    this.currentPage.update((pageNo) => pageNo + 1);
  }

  goBack() {
    if (!this.canGoBack()) return;
    this.currentPage.update((pageNo) => pageNo - 1);
  }
}

テンプレートでは以下のようにボタンの属性にも上記のロジックを適応させます。

app.component.html
<h1>Current Page</h1>
<p>{{ this.currentPage() }}</p>
<nav>
  <button (click)="this.goBack()" type="button" [disabled]="!this.canGoBack()">Back</button>
  <button (click)="this.goForward()" type="button" [disabled]="!this.canGoForward()">Next</button>
</nav>

結果

ezgif.com-video-to-gif (1).gif

とても簡単で素晴らしい!

まとめ

徐々にAngularのSignalが具体性を高めてきていますが、ワクワクが絶えません。

やっぱり上記のような同期処理を各ロジックが非常に簡単になりそうなので助かります。

また、Vueにも似たような文法なので、筆者は受け入れやすい感じです。

みんなもぜひ試してみてください!

8
2
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
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?