Angular7 で Web アプリを作ろう - mat-datepicker 年月のみ版
今日すること
こんにちは、ふるてつです。
今回もAngular Material
です。
mat-datepicker
で年月のみのカレンダーを表示する方法について書きました。
mat-datepickerのリファレンスについて
年月だけの「datepicker」の作り方は「datepicker」のリファレンスの上段、「Datepicker emulating a Year and month picker」の所に記述があります。
https://material.angular.io/components/datepicker/overview
サンプルのソースにはhtml
、ts
の例がありますので、それにしたがって書いていきます。
カレンダーのコンポーネントを追加
まずは下記のコマンドでコンポーネントを追加します。
ng generate component ./component/common/date/MatDatepickerMonth
まずhtml
ですが、下記のようにリファレンスの内容をほぼそのまま写します。
mat-datepicker-month.component.html
の内容
<form [formGroup]="mainForm">
<mat-form-field>
<input id="yearMonth" matInput [matDatepicker]="dp" placeholder="{{ this.placeholder | translate }}"
formControlName="yearMonth" (dateInput)="addEvent('input', $event)" (dateChange)="addEvent('change', $event)">
<mat-datepicker-toggle matSuffix [for]=" dp"></mat-datepicker-toggle>
<mat-datepicker #dp startView="multi-year" (yearSelected)="chosenYearHandler($event)"
(monthSelected)="chosenMonthHandler($event, dp)" panelClass="example-month-picker">
</mat-datepicker>
</mat-form-field>
</form>
全体を<form [formGroup]="mainForm"></form>
で囲んだ点と、placeholderを親画面から指定できるようにした点がリファレンスとは少し違います。
placeholder="{{ this.placeholder| translate }}
あとmatInput
を直接手で編集した場合に親画面と連携するためのイベントを追加しました。
(dateInput)="addEvent('input', $event)" (dateChange)="addEvent('change', $event)"
次にts
ファイルの内容を書きます。
mat-datepicker-month.component.ts
の内容
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { FormBuilder, Validators, FormControl } from '@angular/forms';
import { MomentDateAdapter } from '@angular/material-moment-adapter';
import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core';
import { MatDatepicker } from '@angular/material/datepicker';
import { MatDatepickerInputEvent } from '@angular/material/datepicker';
// Depending on whether rollup is used, moment needs to be imported differently.
// Since Moment.js doesn't have a default export, we normally need to import using the `* as`
// syntax. However, rollup creates a synthetic default module and we thus need to import it using
// the `default as` syntax.
import * as _moment from 'moment';
// tslint:disable-next-line:no-duplicate-imports
import { default as _rollupMoment, Moment } from 'moment';
const moment = _rollupMoment || _moment;
export const MY_FORMATS = {
parse: {
dateInput: 'YYYY/MM',
},
display: {
dateInput: 'YYYY/MM',
monthYearLabel: 'YYYY MMM',
dateA11yLabel: 'LL',
monthYearA11yLabel: 'YYYY MMMM',
},
};
@Component({
selector: 'app-mat-datepicker-month',
templateUrl: './mat-datepicker-month.component.html',
styleUrls: ['./mat-datepicker-month.component.css'],
providers: [{ provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] },
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS }]
})
export class MatDatepickerMonthComponent implements OnInit {
@Input() locale: String;
@Input() placeholder: string;
@Output() event = new EventEmitter();
yearMonth = new FormControl(moment());
mainForm = this.formBuilder.group({
yearMonth: this.yearMonth,
});
constructor(
private formBuilder: FormBuilder,
private adapter: DateAdapter
) { }
ngOnInit() {
this.adapter.setLocale(this.locale);
this.event.emit(this.yearMonth.value);
}
chosenYearHandler(normalizedYear: Moment) {
const ctrlValue = this.yearMonth.value;
ctrlValue.year(normalizedYear.year());
this.yearMonth.setValue(ctrlValue);
}
chosenMonthHandler(normalizedMonth: Moment, datepicker: MatDatepicker) {
const ctrlValue = this.yearMonth.value;
ctrlValue.month(normalizedMonth.month());
this.yearMonth.setValue(ctrlValue);
this.event.emit(this.yearMonth.value);
datepicker.close();
}
addEvent(type: string, event: MatDatepickerInputEvent) {
this.event.emit(this.yearMonth.value);
}
}
こちらも基本的にリファレンスの内容をそのままうつします。
異なる点は@Input() locale: String;
を追加し、他言語化のlocale
を親画面から受け取れるようにしたのと、
@Input() placeholder: string;
は名前のとおりplaceholder
を親画面から指定できるようにした点です。
あと@Output() event = new EventEmitter<String>();
は親画面にイベントを通知するために追加しました。
@Input()
は直感的に解るのですが、@Output()
はそれ用のメソッドを親子ともに作らないとならなく、ひとくせあるなあという感じです。
親画面との連携
親画面のhtml
は下記のようにしました。
量が多いので呼び出している個所だけ抜粋しました。
<div class="example">
<app-mat-datepicker-month [locale]='locale' [placeholder]='displayNameMonthFrom'
(event)="onReceiveEventFromChild($event)">
</app-mat-datepicker-month>
</div>
次にts
ですがこちらも抜粋です。
...
export class OyagamenComponent implements OnInit {
public monthFrom = new FormControl('', []);
...
public locale: String = 'ja-JP';
public displayNameMonthFrom: String = 'evaluationResultScreen.monthFrom';
constructor(
...) {
}
ngOnInit(
...) {
}
onReceiveEventFromChild(eventData: String) {
this.monthFrom.setValue(eventData);
}
}
public monthFrom = new FormControl('', []);
が年月を子画面からうけとる変数です。
public locale: String = 'ja-JP';
は子画面にわたすロケール(一旦親画面に直接書きました)
public displayNameMonthFrom: String = 'evaluationResultScreen.monthFrom';
は子画面のplaceholderにわたすための変数です。
ちなみに'evaluationResultScreen.monthFrom'
は他言語化のためのja.json
中の固定値です。
onReceiveEventFromChild
は子画面のイベントを受け取ったときに動くメソッドです。
このメソッドがmonthFrom
に最新の年月をセットします。
動かしてみる
実際に動かしてみると下記のようになります。
まず年を選択します。
年を選択すると次は月
月を選択するとカレンダーが閉じ、「開始年月」という項目にいま選択した年月が設定されました。
今日の感想
今日はmat-datepicker
で年月のみのカレンダーを表示する方法でした。
マテリアルのリファレンス通りにすればわりと簡単にできます。
わたしはせっかくなので何度も使えるようコンポーネント化したのですが、そちらの方が少し時間がかかりました。
親画面に@Output()で値を渡すところで、なかなかイベントが伝わらずそこですこし手が止まりました。
一度動くようになったらああこんなものかと思いましたが、動くまでは若干試行錯誤した感じでした。
では、今日もお疲れ様でした。