Angular PR

【Angular】ModelSignalで実現する双方向バインディング入門

記事内に商品プロモーションを含む場合があります

こんにちは!

Angularアプリケーション開発において、コンポーネント間のデータ共有はますます重要な要素となっています。

Angularのシグナルって何だろう?ModelSignalってどう使うの?
双方向バインディングの新しい方法があるって聞いたけど…
従来の@Input/@Outputと比べて何が違うの?

Angularの新しいデータバインディング手法について、このような疑問をお持ちではないでしょうか。

この記事では、Angularの新機能「ModelSignal」について、**基本概念から具体的な使い方、従来の方法との違いまで**を詳しく解説します。

この記事は次のような方におすすめです。

この記事はこんな人におすすめ!
  • Angularのシグナルやmodelについて知りたい方
  • 双方向バインディングを最新のAngularで実装したい方
  • コンポーネント間の通信を効率化したい開発者
  • Angular v17以降の新機能に興味がある方
  • リアクティブプログラミングを学びたい方

この記事を読めば、ModelSignalの仕組みが理解でき、実際のプロジェクトですぐに活用できるスキルが身につきます。Angular開発手法を取り入れて、より効率的なコードを書けるようになりましょう。

「コンポーネント間の通信をシンプルにしたい方」「最新のAngular機能を活用したい方」は、ぜひ参考にしてください。

それでは、順を追って詳しく見ていきましょう!

Angularシグナルとは何か

Angularシグナルは、Angularアプリケーションでリアクティブなプログラミングを実現するための新しい機能です。シグナルは値を保持し、その値が変更されたときに通知を発行する特別な種類の変数と考えることができます。

シグナルの基本的な特徴は以下の通りです。

  • 値の変更を監視し、自動的に通知する
  • 依存関係を追跡し、関連するコードを更新する
  • 変更検出を最適化し、パフォーマンスを向上させる
  • コードをよりリアクティブで宣言的にする

シグナルは初めてAngular v16で導入され、その後のバージョンで機能が拡張されてきました。Angular v17.2からは、新たに「ModelSignal」という機能が追加され、双方向データバインディングがよりシンプルになりました。

ModelSignalとは

ModelSignalは、Angular v17.2で導入された機能で、コンポーネント間の双方向データバインディングを実装するための特別なタイプのシグナルです。

ModelSignalは、WritableSignal(書き込み可能なシグナル)を拡張したもので、入力(input)と出力(output)の両方の役割を果たします。これにより、親コンポーネントと子コンポーネントの間でデータを双方向に共有することができます。

ModelSignalの特徴

ModelSignalには以下のような特徴があります。

  • WritableSignalの機能を継承している(set、updateメソッドなど)
  • 値が変更されると自動的に出力イベントを発行する
  • 双方向バインディング構文 [(プロパティ名)] に対応している
  • コンポーネント間の通信を効率化する

従来の双方向バインディングとModelSignalの違い

従来のAngularでは、双方向バインディングを実現するために、@Input装飾子と@Output装飾子を組み合わせて使用していました。これに対し、ModelSignalを使用した方法では、コードを簡略化できます。

以下は、従来の方法とModelSignalを使った方法の比較です。

従来の方法 ModelSignalを使った方法
@Input()と@Output()の組み合わせが必要 model()関数一つで実現可能
イベント発行の処理を明示的に記述 値の変更が自動的にイベントとして発行される
コード量が多くなりがち コード量が少なくなる
ライフサイクルフックが必要な場合がある 多くの場合ライフサイクルフックが不要

ModelSignalの基本的な使い方

ModelSignalを使うためには、まずAngular v17.2以降が必要です。それでは、基本的な使い方を見ていきましょう。

1. インポート

まず、必要なモジュールをインポートします。

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

2. ModelSignalの定義

コンポーネント内でModelSignalを定義します。

@Component({
  selector: 'app-counter',
  standalone: true,
  template: `
    <div>
      <p>カウント値: {{ count() }}</p>
      <button (click)="increment()">増加</button>
      <button (click)="decrement()">減少</button>
    </div>
  `
})
export class CounterComponent {
  count = model(0); // 初期値0のModelSignalを作成

  increment() {
    this.count.update(value => value + 1);
  }

  decrement() {
    this.count.update(value => value - 1);
  }
}

これだけで、双方向バインディングに対応したカウンターコンポーネントができました。

3. 親コンポーネントからの使用

次に、親コンポーネントからこのカウンターコンポーネントを使用してみましょう。

@Component({
  selector: 'app-parent',
  standalone: true,
  imports: [CounterComponent],
  template: `
    <h1>親コンポーネントのカウント: {{ parentCount }}</h1>
    <app-counter [(count)]="parentCount"></app-counter>
  `
})
export class ParentComponent {
  parentCount = 0;
}

ここでは、[(count)]="parentCount" という構文を使って、双方向バインディングを実現しています。これにより、子コンポーネントでカウントが変更されると、親コンポーネントの parentCount も自動的に更新されます。

ModelSignalの高度な使い方

必須のModelSignal

ModelSignalをオプショナルではなく必須にする場合は、model.required を使用します。

export class RequiredInputComponent {
  // 必須のModelSignal
  value = model.required<string>();
}

初期値の設定

ModelSignalに初期値を設定することも可能です。

export class DefaultValueComponent {
  // 初期値「こんにちは」のModelSignal
  greeting = model('こんにちは');
}

エイリアスの設定

コンポーネント内の変数名とテンプレートで使用する名前を分けたい場合は、エイリアスを設定できます。

export class AliasComponent {
  // 'nameAlias'というエイリアスを持つModelSignal
  internalName = model('', { alias: 'nameAlias' });
}

そして、親コンポーネントからは次のように使用します。

<app-alias [(nameAlias)]="personName"></app-alias>

モデルの値を更新する

ModelSignalの値を更新するには、setメソッドまたはupdateメソッドを使用します。

export class UpdateModelComponent {
  counter = model(0);

  // 直接値を設定
  resetCounter() {
    this.counter.set(0);
  }

  // 現在の値に基づいて更新
  incrementCounter() {
    this.counter.update(value => value + 1);
  }
}

具体的なModelSignalの例

それでは、より具体的な例を見てみましょう。以下は、シンプルなフォーム要素を持つコンポーネントの例です。

@Component({
  selector: 'app-text-input',
  standalone: true,
  template: `
    <div>
      <label>{{ label() }}</label>
      <input
        [value]="value()"
        (input)="onInput($event)"
        [placeholder]="placeholder()"
      >
    </div>
  `
})
export class TextInputComponent {
  label = model('ラベル');
  value = model('');
  placeholder = model('ここに入力してください');

  onInput(event: Event) {
    const input = event.target as HTMLInputElement;
    this.value.set(input.value);
  }
}

この例では、ラベル、値、プレースホルダーの3つのModelSignalを持つテキスト入力コンポーネントを作成しています。親コンポーネントからは次のように使用できます。

@Component({
  selector: 'app-form',
  standalone: true,
  imports: [TextInputComponent],
  template: `
    <h2>フォーム</h2>
    <app-text-input
      [(label)]="nameLabel"
      [(value)]="name"
      [(placeholder)]="namePlaceholder"
    ></app-text-input>
    <p>入力された名前: {{ name }}</p>
  `
})
export class FormComponent {
  nameLabel = '名前';
  name = '';
  namePlaceholder = 'あなたの名前を入力してください';
}

親コンポーネントでは、3つのModelSignalすべてに対して双方向バインディングを設定しています。これにより、子コンポーネントでの入力が親コンポーネントにリアルタイムで反映されます。

ModelSignalの内部動作

ModelSignalは内部的にはどのように動作しているのでしょうか?ModelSignalは、実際にはWritableSignalを拡張し、さらに出力機能を追加したものです。

ModelSignalが値を更新すると、以下のことが自動的に行われます。

  1. シグナルの値を更新
  2. 依存する計算やエフェクトを再実行
  3. コンポーネント名+「Change」というサフィックスを持つ出力イベントを発行

例えば、countというModelSignalがあれば、値が変更されるとcountChangeというイベントが発行されます。これが、双方向バインディング [(count)] の仕組みです。

ModelSignalのメリットと注意点

ModelSignalはメリットもありますが、注意点もあります。

メリット

ModelSignalを使用することで、以下のようなメリットがあります。

  • 従来の@Inputと@Outputの組み合わせよりもコードが簡潔になります。
  • TypeScriptの型推論がより効果的に機能します。
  • シグナルベースのアプローチにより、データの流れがより明確になります。
  • 変更検出の最適化により、アプリケーションの応答性が向上します。
  • コンポーネントの状態がより予測可能になり、テストが容易になります。

注意点

ModelSignalを使用する際には、いくつかの注意点があります。

  • Angular v17.2以降が必要です。より古いバージョンのAngularではこの機能は使用できません。
  • transformのサポートがありません。従来の@Inputでサポートされていた値の変換機能はModelSignalではサポートされていません。
  • 過度な使用に注意が必要です。すべての入力にModelSignalを使用すると、データフローが複雑になる可能性があります。
  • デバッグが複雑になることがあります。深いネストでの使用は、値の変更元を追跡するのが難しくなる場合があります。

シグナルとModelSignalの使い分け

Angularでは、複数のタイプのシグナルが提供されています。それぞれの用途に応じて適切なものを選択することが重要です。

シグナルの種類 用途
signal() 一般的なリアクティブな状態管理
computed() 他のシグナルから派生した値の計算
effect() シグナルの変更に応じた副作用の実行
input() コンポーネントへの入力(読み取り専用)
model() 双方向バインディングが必要な場合

ModelSignalは、特に双方向バインディングが必要な場合に最適です。しかし、すべての入力にModelSignalを使用するのではなく、本当に双方向のデータフローが必要な場合に限定して使用することをお勧めします。

ModelSignalと他のリアクティブ手法の比較

Angularには、ModelSignal以外にもリアクティブプログラミングを実現するための手法がいくつかあります。それぞれの特徴を比較してみましょう。

手法 特徴 用途
ModelSignal シンプルな双方向バインディング フォーム要素、UIコントロールなど
RxJS 複雑な非同期イベントの処理 HTTP通信、複雑なイベント処理
NgRx 大規模なアプリケーションの状態管理 複雑なデータフローを持つアプリケーション

それぞれの手法には一長一短があります。ModelSignalは比較的シンプルなケースに最適である一方、より複雑な状態管理やイベント処理には他の手法が適しているかもしれません。

キャリア形成/給与還元
ひとつひとつ真摯に向き合う企業
ONE_WEDGE社員募集

株式会社 ONE WEDGEでは、新たな仲間を募集しています!

私たちと一緒に、革新的で充実したキャリアを築きませんか?
当社は、従業員が仕事と私生活のバランスを大切にできるよう、充実した福利厚生を整えています。

  • 完全週休2日制(土日休み)で、祝日や夏季休暇、年末年始休暇もしっかり保証!
  • 様々な休暇制度(有給、慶弔、産前・産後、育児、バースデー休暇、有給6日取得で特別休暇付与)を完備!
  • 従業員の成長と健康を支援するための表彰制度、資格取得支援、健康促進手当など!
  • 生活を支えるテレワーク手当、記事寄稿手当、結婚祝金・出産祝金など、様々な手当を提供!
  • 自己啓発としての書籍購入制度や、メンバー間のコミュニケーションを深める交流費補助!
  • 成果に応じた決算賞与や、リファラル採用手当、AI手当など、頑張りをしっかり評価!
  • ワークライフバランスを重視し、副業もOK!

株式会社 ONE WEDGEでは、一人ひとりの従業員が自己実現できる環境を大切にしています。
共に成長し、刺激を与え合える仲間をお待ちしております。
あなたの能力と熱意を、ぜひ当社で発揮してください。
ご応募お待ちしております!

ホームページ、採用情報は下記ボタンからご確認ください!

応募、ご質問など、LINEでお気軽にご相談ください♪

まとめ

ModelSignalは、Angularの双方向バインディングを簡略化する機能です。Angular v17.2から導入されたこの機能により、コンポーネント間のデータ共有がシンプルに実装できるようになりました。

  • コードがシンプルになり、メンテナンス性が向上します。
  • 従来の@Input/@Outputの組み合わせを単一の関数で実装できます。
  • 値の変更が自動的にイベントとして発行されるため、コード量が減少します。
  • シグナルベースの仕組みにより、変更検出の最適化が期待できます。
  • フォームコンポーネントやUIコントロールの開発に適しています。

双方向バインディングが必要な場合は、ModelSignalの使用を検討してみてください。コードの量が減少し、データフローの管理がしやすくなります。ただし、すべてのケースでModelSignalを使うのではなく、双方向データバインディングが必要な場合に限定して使用することをお勧めします。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です