前回の投稿 Angular 4 でマテリアルデザインを使うで、とりあえずインストールまでできたので、次に、データのバインディングをマテリアルデザインを適用しながらやってみたい。
最初の落とし穴
Angular4 と マテリアルデザインのインストールのブログはあっても、なかなか、Angular 4 のバインディングと一緒に書いている記事は見当たらなかった。多分みんな慣れているからだろう。マテリアルデザインも、Angular も同時に初心者の人はあまりやらないのかな。そこで、何かを参考に基本的なことをやるわけですが、ここで強烈にはまったのが、プロジェクト間違い。
- AngularJS Material というプロジェクトと、Angular Material は全く別のプロジェクトである。
という事実だ。最初間違えて AngularJS Material を参考にコードを書いてたら、モジュールが見つからないとか出てきて、初心者の私は死亡、もう、REACT にしようかなぁとか頭によぎった。エラーをググってみると、「AngularJS Material と、Angular Materialは別プロジェクト」ということが書いてあった。、、、ムッチャややこしいわ。ちなみに、「AngularJS Material」 はどうやら、Angular 1.x向けのよう。だから、我々は、基本的にAngular Material を参考にすると良い。基本的にドキュメントは丁寧なのでわかりやすい。
フォームの作成
まずは基本的なフォームを作ろうとしている。それをバインディングしてみる。
"Please add a @NgModule annotation" error
最初にやっていると当たったエラー。JavaScript のエラーは理由がよくわからないのも多いなぁ。
詳細は上記の記事だけど、具体的には、tsconfig.json
に path
のエントリを追加する。angular-cli を使っているプロジェクトだと、Linked librariesというのが必要らしく、コンパイルの時のリンクのパスをtsconfig.json
に指定する必要がある。このエラーメッセージからは到底わからないのが辛いところ。
"paths": {
"@angular/*": [
"..node_modules/@angular/*"
]
},
インプットフォームの作成
とりあえず、食事を入力するフォームを作ってみる。まずは、ドメインクラスの定義
export class Food {
name: string;
date: Date;
calorie: number;
protein: number;
carb: number;
fat: number;
meal_kind: FoodType;
}
簡単のため、まずは、名前と日付程度を試してみる。
@Component({
selector: 'app-root',
templateUrl: './food-detail.component.html'
})
export class FoodDetailComponent {
food: Food;
constructor() {
this.food = new Food();
this.food.name = "Taro";
this.food.date = new Date();
}
title = 'Muscle Hack';
}
テキスト入力
テキスト入力を可能にするためには、
が参考になる。テキスト入力のコンポーネントを使うためには、上記のページの API
に書いている通り、import {MatInputModule} from '@angular/material';
を追加する。具体的には下記の通り。前回の記事の通り、私は、material.module.ts
という名前で、マテリアル系のインポートは分離している。
import { NgModule } from '@angular/core';
import {
MdButtonModule,
MdMenuModule,
MdToolbarModule,
MdIconModule,
MdCardModule,
MatInputModule,
} from '@angular/material';
@NgModule({
imports: [
MdButtonModule,
MdMenuModule,
MdToolbarModule,
MdIconModule,
MdCardModule,
MatInputModule,
],
exports: [
MdButtonModule,
MdMenuModule,
MdToolbarModule,
MdIconModule,
MdCardModule,
MatInputModule,
]
})
export class MaterialModule {}
通常の入力フォームはこんな感じでいける。バインディングは、通常の、Angular の時と同じ。
私は、Angular Example - Tour of Heroes: Part 6とかを参考に書いている。
<md-form-field class="example-full-width">
<input mdInput placeholder="Name" name="name" [(ngModel)]="food.name" disabled value="Ushio">
</md-form-field>
これだと、極めてそっけない画面になる。これは悲しい。

もう少し見栄えを良くしたい。じゃあ、とりあえず、ツールバーをつけてみよう。
<md-toolbar colo="primary">
<span>Msucle Hack</span>
</md-toolbar>
<md-form-field class="example-full-width">
<input mdInput placeholder="Name" name="name" [(ngModel)]="food.name" disabled value="Ushio">
</md-form-field>

ぐぬぬ。地味だ、思った色と違う。なんでや!と思ったけど、単にtypo. color
<md-toolbar color="primary">
やっと思った色に。

しかし、まだまだ、ダサい、なんかサンプルと違う。そうだカードを入れてみよう。
それっぽくなった。

Date Picker
次に Date Picker をつけてみたい。やっぱ日付は、部品があるので使いたい。
を同じく参考にする。ただし、今回は、このページの API
で必要とされているモジュール以外にも必要になるモジュールが存在した。
MdNativeDateModule
だ。同じく、material.module.ts
の記述はこう変わった。
import { NgModule } from '@angular/core';
import {
MdButtonModule,
MdMenuModule,
MdToolbarModule,
MdIconModule,
MdCardModule,
MatInputModule,
MdNativeDateModule,
MatDatepickerModule,
} from '@angular/material';
@NgModule({
imports: [
MdButtonModule,
MdMenuModule,
MdToolbarModule,
MdIconModule,
MdCardModule,
MatInputModule,
MdNativeDateModule,
MatDatepickerModule,
],
exports: [
MdButtonModule,
MdMenuModule,
MdToolbarModule,
MdIconModule,
MdCardModule,
MatInputModule,
MdNativeDateModule,
MatDatepickerModule,
]
})
export class MaterialModule {}
次に、food-detail.compent.html
を書き換える
<md-toolbar color="primary">
<span>Msucle Hack</span>
</md-toolbar>
<md-card>
<form class="example-form">
<md-form-field class="example-full-width">
<input mdInput placeholder="Name" name="name" [(ngModel)]="food.name" disabled value="Ushio">
</md-form-field>
<md-form-field>
<input mdInput [(ngModel)]="food.date" name="date" [mdDatepicker]="myDatepicker">
<md-datepicker-toggle mdSuffix [for]="myDatepicker"></md-datepicker-toggle>
<md-datepicker #myDatepicker></md-datepicker>
</md-form-field>
</form>
</md-card>
こちらのハマりどころは、先ほどのライブラリの追加ぐらいで特にない。ちなみに、md-datepicker-toggle
から、mdSuffix
を抜くと見栄えがおかしくなるので、mdSuffix
をつけて、場所を調整すること。
うむ。それっぽい。バインディングもしっかりできている。

うーん。しかし、日本的には表示が違うかな。ロケールを指定しよう。material.module.ts
のところに、次の記述を追加。
providers: [
{provide: MAT_DATE_LOCALE, useValue: 'ja-JP'},
],
もしくは、food-detail.component.ts
のFoodDetailComponent を次のようにする。
export class FoodDetailComponent {
food: Food;
constructor(dateAdapter: DateAdapter<NativeDateAdapter>) {
dateAdapter.setLocale('ja-JP');
this.food = new Food();
this.food.name = "Taro";
this.food.date = new Date();
}
試すとどちらでも動作した。

まとめ
久々に、JavaScript のフロントエンドを触ったが、やっぱデバッグが面倒だ。デバックメッセージからストレートに意味が取れないことが多い。やっぱり、npm start
をして、部品を一つ一つ追加していって、動くのを確認する方が良さげ。というのも、何かを追加したら、画面が出なくなってガサーとエラーがはかれるわけだけど、部品ごとに区切らないと、デバッグがしにくい。これがちょっとした学び。
次は、enum を使って、選択肢を作ってみたいな。