Angularの新機能を触ってみようハンズオン

2024/02/15に公開

この記事は、「Startup Angular #7 LT会&新しいAngularで始めよう!」のイベントのハンズオンで使用するものです。

ハンズオンの目的

このハンズオンの目的は、最近のAngularリリース(v17まで)に含まれる新機能を触ってみることで、よりAngularというフレームワークに興味を持ってもらうことです。

ハンズオンの全体像

このハンズオンは、次の構成で進めていきます。

時間 内容
10分 はじめに&環境構築
5分 コンポーネントを知ろう
10分 Control FlowでTodoリストを作ってみよう
5分 Signalでクッキーのレシピを作ってみよう

事前準備

  • PC
  • ブラウザ(推奨:GoogleChrome)

実行環境の用意

  1. StackBlitzを使う(推奨)
    • StackBlitzは、Webブラウザ上でコードを編集、ビルド、デバッグできるWebアプリケーションのオンライン開発環境です。
    • 手順
      1. Angular Starterを開きます。
      2. プレビューにHello world!が表示されたら環境のセットアップは完了です。
  1. Angular Playgroundを利用する
    • angular.devは2023年11月にAngularの開発チームが立ち上げた、Angular開発者にとっての将来的な拠点となるベータ版のドキュメントサイトです。
    • チュートリアルなどのコンテンツがありますが、今回は最新のAngularをWebブラウザで試せるAngular Playgroundを使用します。
    • 手順
      1. 新規ウィンドウでAngular Playgroundを開きます。
      2. 右上のSelect a templateのセレクトボックスから「Hello world」を選択しましょう。
      3. プレビューにHello world!が表示されたら環境のセットアップは完了です。
    • Angular PlaygroundはあくまでPlaygroundのため、書いたコードは保持されずリロードなどをすると消えてしまいます。残したい場合は、エディター右上のアイコンボタンを押すことで、現在書いたコード含むプロジェクトをzip形式でダウンロードすることができます。

コンポーネントを知ろう(目安時間:5分)

  • コンポーネントは、Angularアプリケーションの基本的な構成要素です。各コンポーネントには次の3つの部分があります。TypeScriptクラス、HTMLテンプレート、CSSスタイルです。
main.ts
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    Hello world!
  `,
})
export class PlaygroundComponent {}

bootstrapApplication(PlaygroundComponent);
  • 初期状態では、PlaygroundComponentというコンポーネントクラスが<app-root>要素で定義されています。そして、bootstrapApplicationでアプリケーションをブートストラップ(起動)されています。
  • Angularでは、コンポーネントをTypeScriptのクラスデコレーター(@Component)を利用して定義します。クラスデコレーターに渡すオブジェクトによって、任意のコンポーネントを定義することができます。コンポーネントのHTMLテンプレートは、templateプロパティに書くことができ、CSSスタイルは、stylesに文字列か、文字列の配列として記述することができます。(また、必要に応じてテンプレートとスタイルを別のファイルに記述してtemplateUrl,styleUrlsで読み込むこともできます。)
  • stylesプロパティを追加して、CSSスタイルを適応してみましょう。
main.ts
import {Component} from '@angular/core';
import {bootstrapApplication} from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
-   Hello world!
+   <p>Hello world!</p>
  `,
+  styles: [`
+      p {
+        color: #a144eb;
+      }
+  `]
})
export class PlaygroundComponent {}

bootstrapApplication(PlaygroundComponent);
  • 次は、クラスプロパティとしてeventNameプロパティを追加し、データをテキストとして表示するための補間構文{{}}を利用して、表示されるテキストを変えてみましょう。
main.ts
import {Component} from '@angular/core';
import {bootstrapApplication} from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
-    Hello world!
+    <p>Hello {{ eventName }}!</p>
  `,
  styles: [`
      p {
        color: #a144eb;
      }
  `]
})
export class PlaygroundComponent {
+    eventName = 'StartupAngular';
}

bootstrapApplication(PlaygroundComponent);

Hello StartupAngular!が表示されたら、この章は完了です!

Control FlowでTodoアプリケーションを作ろう(目安時間:10分)

  • ここからはAngular17から開発者プレビューで導入されたControl Flow(制御フロー)の構文でTodoアプリケーションを実際に作っていきましょう。

0. 実行環境の用意

実行環境の用意の手順完了後のHello World!が表示されている状態にしましょう。

1. Todoリストを表示してみよう

  • まず、PlaygroundComponentクラスにtodoを持つ配列todosプロパティを追加します。
  • そして、テンプレート内の要素を繰り返しを表現するためのControl Flowの構文@forを利用して、Todoリストを表示してみましょう。trackは必須項目となっており、ループするリスト内の要素で一意な値を設定します。ここでは、$indexというコンテキスト変数を用いて、要素のインデックスを設定しています。(2つのコレクションの差分を計算するための track)
  • Control Flowの構文@emptyブロックでは、リストの中身がない場合に表示される要素を定義します。
main.ts
import {Component} from '@angular/core';
import {bootstrapApplication} from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
-    Hello world!
+    <h2>Todos</h2>
+    @for (todo of todos; track $index) {
+        <p>{{ todo.text }}</p>
+    } @empty {
+        <p>No todos</p>
+    }
  `,
})
export class PlaygroundComponent {
+  todos: Array<{text: string, done: boolean}> = [
+    { text: "StartupAngularに参加する", done: true },
+    { text: "イベントアンケートに答える", done: false }
+  ];
}

bootstrapApplication(PlaygroundComponent);

2. Todoを追加できるようにしてみよう

  • まず、指定したTodoを入力できるようにするために、テンプレートに<input>要素を追加します。#textは、テンプレート変数と呼ばれるもので、これを宣言することで<button>要素などテンプレート内の任意の場所でテンプレート変数を参照することができるようになります。今回はHTMLの<input>要素に対して宣言し、その要素の現在のvalue、値を利用します。
  • そして、入力したTodoを追加するための<button>を追加し、クリックした時のイベントをハンドリングするために(click)イベントバインディングを設定して、クリックした時にTodoリストに入力項目を追加できるようにするadd関数を追加します。
  • Todoリストに追加できるようになったのでtodosの中身を空にします。
main.ts
import {Component} from '@angular/core';
import {bootstrapApplication} from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
     <h2>Todos</h2>
+    <input #text />
+    <button (click)="add(text.value)">Add</button>

     @for (todo of todos; track $index) {
         <p>{{ todo.text }}</p>
     } @empty {
         <p>No todos</p>
     }
  `,
})
export class PlaygroundComponent {
  todos: Array<{text: string, done: boolean}> = [
-    { text: "StartupAngularに参加する", done: true },
-    { text: "イベントアンケートに答える", done: false }
  ];

+  add(text: string) {
+    this.todos.push({text, done: false});
+  }
}

bootstrapApplication(PlaygroundComponent);

3. Todoの完了状態を更新できるようにしてみよう

  • まず、Todoが完了しているかの状態を分かりやすくするために、<input type="checkbox">でチェックボックス要素を配置します。
  • そして、チェックボックス要素の状態をTodoオブジェクトのdoneプロパティと同期するために、[(ngModel)]を追加します。これはFormsModuleを読み込むことで利用できるためコンポーネントで読み込むためのimportsを追加します。ngModelは、双方向データバインディングを提供するディレクティブです。双方向データバインディングは、[(ngModel)]のように記述します。
  • 加えて、Todoが完了した場合は打ち消し線をつけるために、テンプレートで条件付き表示を表現するためのControl Flowの構文@ifを利用して、完了した時は<s>要素で、未完了の時は<span>要素を表示するようにしてみましょう。
main.ts
import {Component} from '@angular/core';
+ import {FormsModule} from '@angular/forms';
import {bootstrapApplication} from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  standalone: true,
+  imports: [FormsModule],
  template: `
     <h2>Todos</h2>
     <input #text />
     <button (click)="add(text.value)">Add</button>

     @for (todo of todos; track $index) {
-         <p>{{ todo.text }}</p>
+         <p>
+           <input type="checkbox" [(ngModel)]="todo.done" />
+           @if (todo.done) {
+           <s>{{ todo.text }}</s>
+           } @else {
+           <span>{{ todo.text }}</span>
+           }
+         </p>
     } @empty {
         <p>No todos</p>
     }
  `,
})
export class PlaygroundComponent {
  todos: Array<{text: string, done: boolean}> = [];

  add(text: string) {
    this.todos.push({text, done: false});
  }
}

bootstrapApplication(PlaygroundComponent);

画像のようなアプリケーションができたら、この章は完了です!

Signalsでクッキーのレシピを表示してみよう(目安時間:5分)

  • ここからはAngular16から導入され、17で安定版になった(一部は開発者プレビュー)のSignalsを使って、クッキーのレシピを表示する簡単なアプリケーションを実際に作っていきましょう。

0. 実行環境の用意

実行環境の用意の手順完了後のHello World!が表示されている状態にしましょう。

1.クッキーの枚数を表示するスライダーを作ってみよう

  • <input type="range">で10~100までのクッキーの枚数を10ごとに変動させるスライダーを追加し、それによってcountの値を変動させるために書き込み可能Signalを使います。
  • スライダーの値をcountの値と同期させるために、input要素のvalueにSignalの値を取得するためにcount()を設定します。
  • また、スライダーで入力値が変化したイベントをハンドリングするために(input)データバインディングを設定して、入力値が変化した時にcountのSignalの値を更新するcount.set()を実行するupdate関数を追加します。
main.ts
- import {Component} from '@angular/core';
+ import {Component, signal} from '@angular/core';
import {bootstrapApplication} from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
-    Hello world!
+    <h2>Cookie recipe</h2>
+
+    <label>
+      # of cookies:
+      <input type="range" min="10" max="100" step="10" [value]="count()" (input)="update($event)" />
+      {{ count() }}
+    </label>
  `,
})
export class PlaygroundComponent {
+  count = signal(10);
+
+  update(event: Event) {
+    const input = event.target as HTMLInputElement;
+    this.count.set(parseInt(input.value));
+  }
}

bootstrapApplication(PlaygroundComponent);

2.クッキーの枚数によって変動する材料を表示してみよう

  • 次に、クッキーの枚数によって変動するbutter(バター)、sugar(砂糖)、flour(小麦粉)の値を変動させるために算出Signalを追加します。
  • 算出Signalは、computedを使うことで、countが更新されるたびに値を更新することができます。
main.ts
- import {Component, signal} from '@angular/core';
+ import {Component, computed, signal} from '@angular/core';
import {bootstrapApplication} from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
  <h2>Cookie recipe</h2>

  <label>
    # of cookies:
      <input type="range" min="10" max="100" step="10" [value]="count()" (input)="update($event)">
          {{ count() }}
  </label>
+
+    <p>Butter: {{ butter() }} cup(s)</p>
+    <p>Sugar: {{ sugar() }} cup(s)</p>
+    <p>Flour: {{ flour() }} cup(s)</p>
  `,
})
export class PlaygroundComponent {
    count = signal(10);
+
+    butter = computed(() => this.count() * 0.1);
+    sugar = computed(() => this.count() * 0.05);
+    flour = computed(() => this.count() * 0.2);

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

bootstrapApplication(PlaygroundComponent);

画像のようなアプリケーションができたら、この章は完了です!

時間が余った方向け

  • TodoアプリのTodoを削除できるようにしてみましょう。
  • angular.devのチュートリアル(ハンズオンで触れていない部分からのリンク)を先に進めてみましょう。

最後に

  • 以上でこのハンズオンは完了です。お疲れ様でした!
  • このハンズオンでは、最近のAngularリリース(v17まで)に含まれる新機能を主に使って簡単なアプリケーションを作ってみました。今回紹介した機能以外にも多くの機能があるので、より興味を持った方は、参考資料をご覧ください。

参考資料

Voicyテックブログ

Discussion