✋
Angularの新機能を触ってみようハンズオン
この記事は、「Startup Angular #7 LT会&新しいAngularで始めよう!」のイベントのハンズオンで使用するものです。
ハンズオンの目的
このハンズオンの目的は、最近のAngularリリース(v17まで)に含まれる新機能を触ってみることで、よりAngularというフレームワークに興味を持ってもらうことです。
ハンズオンの全体像
このハンズオンは、次の構成で進めていきます。
時間 | 内容 |
---|---|
10分 | はじめに&環境構築 |
5分 | コンポーネントを知ろう |
10分 | Control FlowでTodoリストを作ってみよう |
5分 | Signalでクッキーのレシピを作ってみよう |
事前準備
- PC
- ブラウザ(推奨:GoogleChrome)
実行環境の用意
- StackBlitzを使う(推奨)
- StackBlitzは、Webブラウザ上でコードを編集、ビルド、デバッグできるWebアプリケーションのオンライン開発環境です。
- 手順
- Angular Starterを開きます。
- プレビューに
Hello world!
が表示されたら環境のセットアップは完了です。
- Angular Playgroundを利用する
- angular.devは2023年11月にAngularの開発チームが立ち上げた、Angular開発者にとっての将来的な拠点となるベータ版のドキュメントサイトです。
- チュートリアルなどのコンテンツがありますが、今回は最新のAngularをWebブラウザで試せるAngular Playgroundを使用します。
- 手順
- 新規ウィンドウでAngular Playgroundを開きます。
- 右上のSelect a templateのセレクトボックスから「Hello world」を選択しましょう。
- プレビューに
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まで)に含まれる新機能を主に使って簡単なアプリケーションを作ってみました。今回紹介した機能以外にも多くの機能があるので、より興味を持った方は、参考資料をご覧ください。
Discussion