もくじ
- #01 Hello Worldしてみよう!
- #02 雛形アプリのソースコードを覗いてみよう!
- #03 Todoアプリ開発開始!新しくコンポーネントを作ってみよう!
- #04 コンポーネントを連携させてみよう!
- #05 Coming Soon…
今回のゴール
前回までで、ng new
コマンドで雛形アプリを作り、そのコードを読み解くことでAngularアプリがどのような仕組みで動作するのかを学んできました。
いよいよ今回からは実際のアプリケーション開発に入っていきます!
この連載では、簡単なTodoアプリを作っていきます。今回は雛形アプリに1つだけ新しくコンポーネントを追加して、その動作を確認するところまでです。
前回、Angularアプリがモジュールとコンポーネントの組み合わせで作られていることを学びましたが、今回はコンポーネントの中身がどのようになっているのかをより深く理解することを目指しましょう
初めてのコンポーネント作成
早速コンポーネントを新規作成してみましょう。今回はTodoアプリのうち、Todoを編集するためのフォーム のコンポーネントを作成していきます。
ちなみに、ここから先は、作業によるコード差分を管理しやすいよう、gitなどを使ってコードをバージョン管理しながら手を動かしていくことをおすすめします
さて、ng
コマンドには、ng generate
という様々なファイルを自動生成するためのコマンドが用意されています。新しくコンポーネントを作成する際には ng generate component
コマンドを使うのが便利です。
プロジェクトのルートディレクトリ(/path/to/angular-todo
直下)にいる状態で、以下のコマンドを実行してみてください。
$ ng generate component components/todo-form
実行すると、以下のようなディレクトリ構成で4つのファイルが生成されます。
$ tree src/app/components
src/app/components
└── todo-form
├── todo-form.component.html
├── todo-form.component.scss
├── todo-form.component.spec.ts
└── todo-form.component.ts
前回学んだとおり、コンポーネントとは「HTMLテンプレート・スタイル・ロジックをひとまとめにしたUI部品」のことでした。
ng generate component
コマンドで生成した4つのファイルのうち、todo-form.component.ts
がコンポーネントのロジックを記述する本体のクラスファイルです。中身を覗いてみましょう。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-todo-form',
templateUrl: './todo-form.component.html',
styleUrls: ['./todo-form.component.scss']
})
export class TodoFormComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
TodoFormComponent
クラスが定義されていますが、中身は特に何も処理が書かれていない状態になっています。
その上の @Component({})
で囲まれた部分を見てみると、前回コードリーディングしたルートコンポーネント(AppComponent
)と同じように、
selector: 'app-todo-form',
templateUrl: './todo-form.component.html',
styleUrls: ['./todo-form.component.scss']
といった記述がありますね。
どうやら templateUrl
styleUrls
によって、コンポーネントをレンダリングするためのHTMLテンプレートと、そのテンプレートに適用するスタイルを指定するようです
そして selector: 'app-todo-form'
ですが、ここは AppComponent
においては selector: 'app-root'
となっていた部分です。ここで指定した文字列が、このコンポーネントをビューに配置する際のタグ名になります。
つまり、別のコンポーネントのHTMLテンプレート内に <app-todo-form></app-todo-form>
というタグを書くと、その箇所に TodoFormComponent
を挿入することができるというわけです
COLUMN
ここでは、
ng generate component components/todo-form
というコマンドでTodoFormComponent
を生成しましたが、単にng generate component todo-form
とだけ書いても実行可能です。 作りたいコンポーネントの名前の前に、今回のようにcomponents/
などと格納箇所を示すディレクトリパス(src/app
からの相対パス)を付け足すことで、任意の場所にコンポーネントのファイル群を生成することができるのです。今回はより整理しやすいディレクトリ構成とするために、
components/
配下にコンポーネントのファイル群を格納するようにしました。
ng generate component
コマンドで利用できるオプションなど、より詳しい情報は 公式のリファレンス をご参照ください。
モジュールへの登録
ところで、コンポーネントは作るだけではダメで、モジュールに登録することでそのモジュール内で利用できるようになる のでしたよね。今回作った TodoFormComponent
も当然ながらモジュールに登録することが必要です。
雛形アプリの時点で git commit
していた人は、この時点で git diff
してみてください。src/app/app.module.ts
に以下のような差分が出ているのではないでしょうか。
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
+ import { TodoFormComponent } from './components/todo-form/todo-form.component';
@NgModule({
declarations: [
- AppComponent
+ AppComponent,
+ TodoFormComponent
],
imports: [
BrowserModule
そう、ng generate component
コマンドを使ってコンポーネントを生成すると、自動でルートモジュールにコンポーネントを登録してくれる のです
これで、ルートモジュール内で TodoFormComponent
を自由に使えるようになりました。
作ったコンポーネントをアプリに組み込んでみる
では、いよいよ TodoFormComponent
をアプリに組み込んで表示させてみましょう。
src/app/app.component.html
の中身を丸ごと削除して、<app-todo-form></app-todo-form>
という一行だけが書かれた状態にしてみてください。
- <!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
- <!-- * * * * * * * * * * * The content below * * * * * * * * * * * -->
- <!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * -->
- <!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * * -->
:
略
:
- <!-- * * * * * * * * * * End of Placeholder * * * * * * * * * * * -->
- <!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
-
-
+ <app-todo-form></app-todo-form>
ng serve --open
で以下のような画面が表示されていれば成功です
TodoFormComponent
のHTMLテンプレートである src/app/todo-form/todo-form.compoentn.html
の中身を覗いてみてください。
<p>todo-form works!</p>
となっていますね。
ルートコンポーネントのHTMLを <app-todo-form></app-todo-form>
という一行だけにしたので、TodoFormComponent
のHTMLだけが表示されている状態になったわけです
Todoオブジェクトの型を決めておく
さて、今後いくつかのコンポーネントを追加してTodoアプリを作り込んでいくわけですが、その際にやり取りするデータの型が決まっていると何かと楽になるので、今のうちにTodoオブジェクトの型を作っておきましょう。
$ mkdir src/app/models
$ touch src/app/models/todo.ts
src/app/models/todo.ts
というファイルを作って、以下のようなコードを書いてください。
export interface Todo {
name: string;
isDone: boolean;
}
この、name
と isDone
という2つのプロパティを持った Todo
インターフェースを使ってデータのやり取りをしていきます
Todoオブジェクトを画面から編集できるようにする
それではいよいよ、Todoオブジェクトを画面から編集できるようにしていきましょう
コンポーネントクラスを修正
src/app/components/todo-form/todo-form.component.ts
に以下のコードを追記してみてください。
import { Component, OnInit } from '@angular/core';
+ import { Todo } from '../../models/todo';
@Component({
selector: 'app-todo-form',
templateUrl: './todo-form.component.html',
styleUrls: ['./todo-form.component.scss']
})
export class TodoFormComponent implements OnInit {
+
+ public todo: Todo = {
+ name: '',
+ isDone: false,
+ };
constructor() { }
ngOnInit() {
}
}
追記したコードの意味は以下のとおりです。
- 先ほど作成した
Todo
インターフェースの定義ファイルをimportした上で -
Todo
型のpublicなクラス変数todo
を宣言し - 初期値として
{ name: '', isDone: false }
を代入している
まだTypeScriptの文法に戸惑いがあるかもしれませんが、やっていること自体はごく簡単ですね
HTMLテンプレートを修正
では続いて、画面を作り込んでいきましょう。
src/app/components/todo-form/todo-form.component.html
を以下のように修正してください。
- <p>todo-form works!</p>
+ <input type="text" [(ngModel)]="todo.name">
+ <input type="checkbox" [(ngModel)]="todo.isDone">
<input>
タグを使って、Todoの名前入力用のテキストフィールドと完了済みかどうかのチェックボックスを設置しています。
が、それぞれに何やら [(ngModel)]="todo.name"
[(ngModel)]="todo.isDone"
という記述がありますね
実は、ngModel
はAngularに標準で用意されているディレクティブ(ビューで利用できる命令の一種)の1つで、双方向バインディング を実現するもっとも基本的な手段になります
双方向バインディングとは、コンポーネント本体とビューの間でデータを同期する仕組みのことで、この場合、画面上でテキストフィールドの値を書き換えたり、チェックボックスのON/OFFを切り替えたりするたびに、TodoFormComponent
クラスのクラス変数 todo
の name
や isDone
の値がリアルタイムで変更されます。
ちなみに、「双方向 」というだけあって、逆にクラス内で todo
に何かを代入する処理を実行すると、画面側にもそれが反映されます。(今回は、{ name: '', isDone: false }
を初期値として代入しているので、画面の初期表示は 名前:空欄
完了済:チェックなし
となるはずです )
ngModelを使うために、FormsModuleをインポート
先ほど、双方向バインディングを実現するためにHTMLテンプレート内で ngModel
を使用しましたが、実はこの機能は ng new
しただけの雛形アプリには含まれていません。
ngModel
を利用するためには、Angular標準の FormsModule
というモジュールを追加でインポート する必要があります
Angularには「モジュール」という機構があり、必要に応じて複数のモジュールを組み合わせてアプリを構築できるようになっていることは前回ざっくりお伝えしたかと思います。「モジュールを組み合わせる」と表現しましたが、より具体的には、あるモジュールに他のモジュールをインポートする ことによってそれを実現します。
今回は、AppModule
に FormsModule
をインポートする ことで、AppModule
内で FormsModule
が持っている ngModel
という機能を使えるようにしておきます
具体的には、src/app/app.module.ts
に以下のようなコードを追記すればOKです。
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
+ import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { TodoFormComponent } from './components/todo-form/todo-form.component';
@NgModule({
declarations: [
AppComponent,
TodoFormComponent
],
imports: [
BrowserModule,
+ FormsModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
@NgModule({})
の中の imports: []
という配列に、FormsModule
を追加しただけですね
動作確認
ここまでで、無事にTodoオブジェクトを画面から編集できるようになりました!
画面の状態はこんな感じになっているかと思います。テキストフィールドとチェックボックスで、todo
変数の中身を変更することができます。
…が、実際に変更できているのかどうかが見えないので、何の手応えもありませんね
というわけで、todo
変数の中身を画面に表示するようにして、実際に値が変更されていることを目視できるようにしてみましょう
src/app/components/todo-form/todo-form.component.html
に以下の一行を追記してみてください。
<input type="text" [(ngModel)]="todo.name">
<input type="checkbox" [(ngModel)]="todo.isDone">
+ {{ todo|json }}
{{ }}
は、コンポーネントが持つクラス変数をビューに表示するためのAngularのテンプレート記法です。
単に {{ todo }}
としてしまうと、オブジェクトである todo
を文字列として表現できず [object Object]
といった表示になってしまうため、ここでは Angular標準の json
パイプ を使って、todo
の中身をJSON形式で表現した文字列を画面に表示するようにしてあります。
この状態で画面を操作してみると、以下のように画面の入力に合わせて todo
の中身が同期的に変更されていることが分かると思います。これが双方向バインディングです
次回予告
というわけで、今回は実際のTodoアプリを作るにあたり、Todoを編集するためのコンポーネントを新たに作成し、中身を実装してきました。コンポーネントをアプリに組み込む手順や、クラスファイルとHTMLテンプレートの関係などが前回よりも深くご理解いただけたのではないかと思います。
次回 はTodoアプリの開発をさらに進めるべく、Todo一覧画面の実装に取り組んでいきます。
お楽しみに!