📘

Angular v20の新機能と開発者体験の向上

に公開

Angular v20がリリースされました!今回のリリースでは、シグナルの可能性をさらに押し広げる新機能が多数追加されています。Angular 16でシグナルが導入されて以来、開発者体験を向上させる素晴らしい機能が続々と登場していますね。

本記事では、Angular v20で追加された主要な新機能について、実践的な使用例とともに解説していきます。

1. リソースAPIで非同期処理がもっとシンプルに

シグナルはリアクティブで同期タスクには最適ですが、完了時間が予測できない非同期タスクについてはどうでしょうか。この課題を解決するために、新しい「リソースAPI」が誕生しました。

リソースAPIは、シグナルグラフの一部として非同期の依存関係を宣言するための新しいプリミティブです。リソースを使用することで、ステータス、値、その他のプロパティにシグナルとしてアクセスできるようになり、他のシグナルベースのロジックやテンプレートにシームレスに統合できます。

基本的な使用方法

@Component({
  selector: 'app-user-profile',
  template: `
    @if (userResource.isLoading()) {
      <div>読み込み中...</div>
    } @else if (userResource.error()) {
      <div>エラーが発生しました</div>
    } @else if (userResource.hasValue()) {
      <div>
        <h2>{{ userResource.value().name }}</h2>
        <p>{{ userResource.value().email }}</p>
      </div>
    }
  `
})
export class UserProfileComponent {
  userResource = resource(() => this.fetchUserData());
  
  private fetchUserData() {
    // 非同期データ取得処理
    return this.http.get<User>('/api/user');
  }
}

リソースの状態管理

リソースは以下の状態をで持ちます:

  • isLoading: データ取得中かどうか
  • error: エラーが発生した場合のエラー情報
  • hasValue: 値が取得できたかどうか
  • value: 取得した値

これらの状態はすべてシグナルとして提供されるため、テンプレート内でリアクティブに使用できます。

2. HTTPリソースでデータ取得がさらに便利に

リソースAPIをベースに、実験的な新しい「HTTPリソース」も構築されました。これは、データ取得のためにリアクティブにHTTPリクエストを実行できるようにするものです。

基本的な使用方法

@Component({
  selector: 'app-data-display',
  template: `
    @if (dataResource.isLoading()) {
      <div>データを取得中...</div>
    } @else if (dataResource.error()) {
      <div>エラー: {{ dataResource.error().message }}</div>
    } @else if (dataResource.hasValue()) {
      <div>
        {{ dataResource.value() | json }}
      </div>
    }
  `
})
export class DataDisplayComponent {
  dataResource = httpResource(() => '/api/data');
}

3. リソースからのストリーミング対応

リソースプリミティブがさらに進化し、リソースから値をストリーミングできるようになりました。これは、クライアントに応答をストリーミングする必要がある場合に便利です。

ストリーミングの実装例

@Component({
  template: `{{ dataStream.value() }}`
})
export class App {
  // WebSocketの初期化ロジックはここに記述します...
  // ...
  // ストリーミングリソースの初期化
  dataStream = resource({
    stream: () => {
      return new Promise<Signal<ResourceStreamItem<string[]>>>((resolve) => {
        const resourceResult = signal<{ value: string[] }>({
          value: [],
        });

        this.socket.onmessage = event => {
          resourceResult.update(current => ({
             value: [...current.value, event.data]
          });
        };

        resolve(resourceResult);
      });
    },
  });
}

4. シグナルAPIファミリーの安定化

linkedSignal effect afterNextRender afterEveryRender が安定版として昇格し、本番環境で利用可能になりました。

実践的な使用例

@Component({
  selector: 'app-example',
  template: `
    <div>{{ count() }}</div>
    <button (click)="increment()">増加</button>
  `
})
export class ExampleComponent {
  count = signal(0);
  
  constructor() {
    // 値の変更を監視
    effect(() => {
      console.log('カウントが変更されました:', this.count());
      this.updateAnalytics(this.count());
    });
    
    // レンダリング後の処理
    afterNextRender(() => {
      this.updateDOM();
    });
  }

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

  private updateAnalytics(value: number) {
    // アナリティクス更新処理
  }

  private updateDOM() {
    // DOM更新処理
  }
}

5. シグナルベースフォームの開発中

Angular内でのシグナルのサポートを継続的に拡大しており、フォームシステムの新しいシグナルベースの進化に取り組んでいます。

期待される機能

  • シグナルベースのリアクティビティ

  • より簡潔なフォーム定義

  • パフォーマンスの向上

  • 型安全性の強化

6. ホストバインディングの型チェック対応

ホストバインディングは、コンポーネントやディレクティブのホスト要素に動的な値をバインドすることを可能にする機能です。Angular v20では、ホストプロパティと、ホストバインディングおよびホストリスナーデコレータの両方に対して、型チェックのサポートを追加しています。

型チェックの実装例

@Component({
  ...,
  host: {
    'role': 'slider',
    '[attr.aria-valuenow]': 'value',
    '[class.active]': 'isActive()',
    '[tabIndex]': 'disabled ? -1 : 0',
    '(keydown)': 'updateValue($event)',
  },
})
export class CustomSlider {
  value: number = 0;
  disabled: boolean = false;
  isActive = signal(false);
  updateValue(event: KeyboardEvent) { /* ... */ }
  /* ... */
}

7. 動的コンポーネント作成APIの改善

動的コンポーネント作成APIは、Angularアプリケーションでプログラム的にコンポーネントを作成する強力な方法を提供します。

改善された使用方法

@Component({
  standalone: true,
  template: `Hello {{ name }}!`
})
class HelloComponent {
  name = 'Angular';
}
@Component({
  standalone: true,
  template: `<div id="hello-component-host"></div>`
})
class RootComponent {}
// アプリケーションのブートストラップ
const applicationRef = await bootstrapApplication(RootComponent);
// ホストとして使用するDOMノードを特定
const hostElement = document.getElementById('hello-component-host');
// ApplicationRefからEnvironmentInjectorインスタンスを取得
const environmentInjector = applicationRef.injector;
// ComponentRefインスタンスを作成
const componentRef = createComponent(HelloComponent, {hostElement, environmentInjector});
// 最後のステップとして、新しく作成したrefをApplicationRefインスタンスを使用して登録し、
// コンポーネントビューを変更検知サイクルに含めます
applicationRef.attachView(componentRef.hostView);
componentRef.changeDetectorRef.detectChanges();

8. その他のQOL機能

TypeScript 5.8のサポート

最新のTypeScript 5.8がサポートされ、より良い型推論と開発体験を提供します。

テンプレート内でのタグなしテンプレートリテラル式

テンプレート内でのタグなしテンプレートリテラル式のサポートも追加されました。

<div>${user.name} さんの残高: ${balance | currency}</div>

テンプレートHMRがデフォルトで有効

テンプレートHMR(Hot Module Replacement)はデフォルトで有効になっており、開発中のリロード時間が大幅に短縮されます。

未使用インポートのクリーンアップ

アプリケーション内の未使用のインポートをクリーンアップする新しいschematicも追加されました。

ng generate @angular/core:cleanup-unused-imports

まとめ

Angular v20は、シグナルベースの開発体験をさらに向上させる素晴らしいリリースです。リソースAPIやHTTPリソース、シグナルベースフォームの開発など、これからのAngular開発がますます楽しくなりそうです。

特に以下の点が印象的です:

  1. 非同期処理の簡素化

    • リソースAPIによる統一的な非同期処理の管理
    • HTTPリソースによるデータ取得の簡素化
    • ストリーミング対応によるリアルタイムデータ処理
  2. 開発者体験の向上

    • ホストバインディングの型チェック
    • 動的コンポーネント作成APIの改善
    • テンプレートHMRのデフォルト有効化
  3. パフォーマンスの最適化

    • シグナルベースのリアクティビティ
    • より効率的な変更検知
    • バンドルサイズの最適化

これらの新機能を使って、ユーザーに本当に愛されるアプリケーションを作成していきたいですね。次に何を構築できるか、今から楽しみです!

Discussion