はじめに
Angularチュートリアル:Tour of Heroesを読み進めていきます。
開発環境
- OS: Windows10
- Nodeバージョン: 18.18.0
- Angular CLI バージョン: 17.3.8
- エディタ: VSCode
1. ヒーローエディター
1. ヒーローエディターを読んでいきます。
関連コマンド
コンポーネント作成
ng generate component コンポーネント名
コンポーネントを作成したいフォルダに行き、このコマンドを実行することで、新規コンポーネントのフォルダが作成されます。
ソースコードを読み解いてみる
インターフェース
インターフェースについて
「このオブジェクトはこういう値を持っている」という制限をかけるための手法です。
たとえば、Studentというオブジェクトがあり、それは出席番号を保持するidという変数を持っているとします。
出席番号なので数字が入ってほしいところですが、ここに数字以外の値("Hello"といった文字列など)が入ってこられると困るわけです。
それを防ぐために、「このオブジェクトはこういう値を持っている」という制限が必要になる場合があり、それを実現するための手段がインターフェースです。
チュートリアルではnumber型のid、string型のnameを持つHeroインターフェースが定義されています。
export interface Hero {
id: number; // 識別番号を保持する変数。型はnumber(数字)
name: string; // 名前を保持する変数。型はstring(文字列)
}
インターフェースの利用
インターフェースを定義したからには、当然それを使う人がいる訳です。
import { Component } from '@angular/core';
import { Hero } from '../hero'; // Heroインターフェースをimport
@Component({
selector: 'app-heroes', // app-heroesというタグを使うことで、heroコンポーネントの内容を参照できる。
templateUrl: './heroes.component.html', // 同フォルダのhtmlファイルを読み込む。
styleUrls: ['./heroes.component.css'] // 同フォルダのcssファイルを読み込む
})
export class HeroesComponent {
/*
* heroオブジェクトを定義。
* heroの実態はHeroインターフェースであり、その定義に乗っ取ってnumber型のidと、string型のnameを持つ。
*/
hero: Hero = {
id: 1,
name: 'Windstorm'
}
}
利用方法は、ざっくばらんに書くと以下のような感じです。
- インターフェースをimportする。
- オブジェクトの型としてそのインターフェースを指定し、値を初期化。
<h2>{{ hero.name }} Detail</h2>
<div><span>id: {{ hero.id }}</span></div>
<div><span>name: {{ hero.name }}</span></div>
ts側でデータ構造が変わったので、それを参照するhtmlにも手を加える必要があります。
- ダブルカーリーグレイシスを使って値を表示するのは相変わらず。
- ドットでつなげて「このオブジェクトのこの値」といった具合で記載します。
双方向データバインディング
双方向データバインディングについて
...
<div>
<label for="name">Hero name: </label>
<input id="name" [(ngModel)]="hero.name" placeholder="name">
</div>
双方向データバインディングについて、チュートリアルでは以下のような解説がされています。
[(ngModel)]は、Angularの双方向データバインディング構文です。
これでhero.nameプロパティをHTMLのテキストボックスにバインドするので、 hero.nameプロパティからテキストボックスへ、テキストボックスからhero.nameプロパティへ、 双方向に データを流すことができます。
双方向データバインディングについて、つまりこういうこと↓ という理解をしました。
- HeroesComponentが保持しているheroオブジェクトのnameがhtmlに参照される。
- その値はテキストボックスの値として格納される。
- テキストボックスの値が変更されると、その値は"hero.name" に格納される。
- これはつまり、HeroesComponentが保持しているheroオブジェクトのnameの値が更新されるということ。
ngModel利用のために必要なこと
FormsModuleのインポートが必要です。
app.module.tsについては後述。
import { FormsModule } from '@angular/forms';
...
NgModule({
imports: [
...,
FormsModule
],
...
})
...
アプリ構成
アプリ構成
ここで、現時点でのアプリ構成を図示するとこのような感じです。
- heroesフォルダ
appフォルダの下にheroesフォルダがあり、そこにheroesコンポーネントのリソースが格納されています(heroes CMPと表記)。 - app-root
app-rootとしているのは、そのようなフォルダが切られているわけではなく、appフォルダ直下にアプリルートのコンポーネントのリソースがあるということを示しています。
土台に部品(heroesコンポーネント)を載せるための記載は、以下のような感じです。
<app-heroes></app-heroes>
<!--
app-heroesとは何ぞや?
→heroes.component.tsのselectorで指定した値と一致
-->
app.module.ts
ここで注目すべきはapp.module.tsです。
開発しているアプリが何を使っているのか(参照しているのか)を記載します。
そのアプリで何が使われているのか、何を参照しているのかという情報を知っているブレーンのような感じでしょうか。
言い方を変えると、たとえ新しいコンポーネントを作って実装したとしても、app.module.tsにそれについての記載がされていないと画面に反映されないということです。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { HeroesComponent } from './heroes/heroes.component';
import { FormsModule } from '@angular/forms';
@NgModule({
declarations: [
AppComponent, // app.component.tsで定義されているクラス
HeroesComponent // heroes.component.tsで定義されているクラス
],
imports: [
BrowserModule, // angularが提供している機能
FormsModule // angularが提供している機能
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
ポイントは以下の通り。
- コンポーネントを作ったらそのクラスをdeclarationsに記載
- 入力フォームなど、angularが提供してくれている機能を使いたい場合はimportsに記載