休日で空いた時間の暇つぶしを探せるアプリを公開しています。
mat-selectを使って、閏年に対応した西暦・月・日のプルダウンを作成します。
記事を読み終わる頃には、閏年にも対応した西暦・月を選択したら動的に日のプルダウンが変わるものができます。
例)2022、3を選択→日の配列が31まで。2020、2→日の配列が29まで。2021、2→日の配列が28まで。2019、4→日の配列が30まで
コード全文
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-date-select',
templateUrl: './date-select.component.html',
styleUrls: ['./date-select.component.css']
})
export class DateSelectComponent implements OnInit {
public years: string[] = [];
public monthes: string[] = [];
public days: string[] = [];
public selectedYear: string;
public selectedMonth: string;
constructor() { }
ngOnInit(): void {
this.yearsList();
this.monthesList();
this.daysList();
}
ngDoCheck(): void {
if (this.selectedYear && this.selectedMonth) {
this.days = [];
// 31日まである月
if (this.selectedMonth === '1' ||
this.selectedMonth === '3' ||
this.selectedMonth === '5' ||
this.selectedMonth === '7' ||
this.selectedMonth === '8' ||
this.selectedMonth === '10' ||
this.selectedMonth === '12') {
for (let i = 1; i <= 31; i++) {
this.days.push(i.toString());
}
}
// 30日まである月
if (this.selectedMonth === '4' ||
this.selectedMonth === '6' ||
this.selectedMonth === '9' ||
this.selectedMonth === '11') {
for (let i = 1; i <= 30; i++) {
this.days.push(i.toString());
}
}
// 2月の場合
if (this.selectedMonth === '2') {
if (this.isLeapYear(this.selectedYear)) {
for (let i = 1; i <= 29; i++) {
this.days.push(i.toString());
}
} else {
for (let i = 1; i <= 28; i++) {
this.days.push(i.toString());
}
}
}
}
}
/**
* 西暦のプルダウンを作成
*
* @return 西暦のプルダウンを今年→1900年になるように配列を作成
*/
public yearsList() {
// 配列を初期化
this.years = [];
// 今日の日付・時刻のオブジェクトを生成
const now = new Date();
// 上記のオブジェクトより年を取得
const year = now.getFullYear();
// 1900年-今年までの配列を作成。i.toString()でiを数値型から文字列型に変換している。
for (let i=1900; i<=year; i++) {
this.years.push(i.toString());
}
return this.years.reverse();
}
/**
* 月の配列を作成
*/
public monthesList() {
this.monthes = [];
for (let i = 1; i <= 12; i++) {
this.monthes.push(i.toString());
}
}
/**
* 日の配列を作成
*/
public daysList() {
this.days = [];
for (let i = 1; i <= 31; i++) {
this.days.push(i.toString());
}
}
/**
* 西暦で選択した値を検知
*
* @param event 選択した年
*/
public changeYear(event: any) {
this.selectedYear = event.value;
}
/**
* 月で選択した値を検知
*
* @param event 選択した月
*/
public changeMonth(event: any) {
this.selectedMonth = event.value;
}
/**
* 閏年か判定
*
* @return trueは閏年、falseは閏年ではない
*/
public isLeapYear(selectedYear: string) {
// 文字列から数値に変換
const year = Number(selectedYear);
if (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) {
return true;
}
return false;
}
}
<mat-form-field>
<mat-label>西暦</mat-label>
<mat-select (selectionChange)="changeYear($event)">
<mat-option *ngFor="let year of years" [value]="year">
{{ year }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-label>月</mat-label>
<mat-select (selectionChange)="changeMonth($event)">
<mat-option *ngFor="let month of monthes" [value]="month">
{{ month }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-label>日</mat-label>
<mat-select>
<mat-option *ngFor="let day of days" [value]="day">
{{ day }}
</mat-option>
</mat-select>
</mat-form-field>
西暦のプルダウンを作成する
西暦のプルダウンを作成しましょう。
ngForを使って、配列から一つずつ年を取得する。[value]で、選択した西暦がプルダウンに表示されるようにする。
<mat-form-field>
<mat-label>西暦</mat-label>
<mat-select>
<mat-option *ngFor="let year of years" [value]="year">
{{ year }}
</mat-option>
</mat-select>
</mat-form-field>
配列は、date-select.component.tsファイルで定義します。
今日の日付や時刻を取得するnew DateやgetFullYear()は、javascriptの知識。
forを使って、西暦の値を配列にプッシュしていく。
.reverse()とすることで、配列の値が逆順になる。1900→2022⇒2022→1900
※reverseしなくても良いですが、例えば2020年を選択する際に、見つけるのが楽だからです。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-date-select',
templateUrl: './date-select.component.html',
styleUrls: ['./date-select.component.css']
})
export class DateSelectComponent implements OnInit {
public years: string[] = [];
constructor() { }
ngOnInit(): void {
this.yearsList();
}
/**
* 西暦のプルダウンを作成
*
* @return 西暦のプルダウンを今年→1900年になるように配列を作成
*/
public yearsList() {
// 配列を初期化
this.years = [];
// 今日の日付・時刻のオブジェクトを生成
const now = new Date();
// 上記のオブジェクトより年を取得
const year = now.getFullYear();
// 1900年-今年までの配列を作成。i.toString()でiを数値型から文字列型に変換している。
for (let i=1900; i<=year; i++) {
this.years.push(i.toString());
}
return this.years.reverse();
}
}
これで、西暦のプルダウンができます。
月のプルダウンを作成する
月のプルダウンを作成します。
<mat-form-field>
<mat-label>西暦</mat-label>
<mat-select>
<mat-option *ngFor="let year of years" [value]="year">
{{ year }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-label>月</mat-label>
<mat-select>
<mat-option *ngFor="let month of monthes" [value]="month">
{{ month }}
</mat-option>
</mat-select>
</mat-form-field>
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-date-select',
templateUrl: './date-select.component.html',
styleUrls: ['./date-select.component.css']
})
export class DateSelectComponent implements OnInit {
public years: string[] = [];
public monthes: string[] = [];
constructor() { }
ngOnInit(): void {
this.yearsList();
this.monthesList();
}
/**
* 西暦のプルダウンを作成
*
* @return 西暦のプルダウンを今年→1900年になるように配列を作成
*/
public yearsList() {
// 配列を初期化
this.years = [];
// 今日の日付・時刻のオブジェクトを生成
const now = new Date();
// 上記のオブジェクトより年を取得
const year = now.getFullYear();
// 1900年-今年までの配列を作成。i.toString()でiを数値型から文字列型に変換している。
for (let i=1900; i<=year; i++) {
this.years.push(i.toString());
}
return this.years.reverse();
}
/**
* 月の配列を作成
*/
public monthesList() {
this.monthes = [];
for (let i = 1; i <= 12; i++) {
this.monthes.push(i.toString());
}
}
}
日のプルダウンを作成する
日のプルダウンを作成します。
<mat-form-field>
<mat-label>西暦</mat-label>
<mat-select>
<mat-option *ngFor="let year of years" [value]="year">
{{ year }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-label>月</mat-label>
<mat-select>
<mat-option *ngFor="let month of monthes" [value]="month">
{{ month }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-label>日</mat-label>
<mat-select>
<mat-option *ngFor="let day of days" [value]="day">
{{ day }}
</mat-option>
</mat-select>
</mat-form-field>
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-date-select',
templateUrl: './date-select.component.html',
styleUrls: ['./date-select.component.css']
})
export class DateSelectComponent implements OnInit {
public years: string[] = [];
public monthes: string[] = [];
public days: string[] = [];
constructor() { }
ngOnInit(): void {
this.yearsList();
this.monthesList();
this.daysList();
}
/**
* 西暦のプルダウンを作成
*
* @return 西暦のプルダウンを今年→1900年になるように配列を作成
*/
public yearsList() {
// 配列を初期化
this.years = [];
// 今日の日付・時刻のオブジェクトを生成
const now = new Date();
// 上記のオブジェクトより年を取得
const year = now.getFullYear();
// 1900年-今年までの配列を作成。i.toString()でiを数値型から文字列型に変換している。
for (let i=1900; i<=year; i++) {
this.years.push(i.toString());
}
return this.years.reverse();
}
/**
* 月の配列を作成
*/
public monthesList() {
this.monthes = [];
for (let i = 1; i <= 12; i++) {
this.monthes.push(i.toString());
}
}
/**
* 日の配列を作成
*/
public daysList() {
this.days = [];
for (let i = 1; i <= 31; i++) {
this.days.push(i.toString());
}
}
}
ここまででとりあえず西暦、月、日のプルダウンが作成できましたが、まだ課題があります。
- 選択された月によって、日のプルダウンで選択できる値を31まで、30までにする
- 2月の場合は、基本的に28までだが閏年の場合は29まで選択できるようにする
これを実現するためには、
選択した西暦や月の値を検知し、その値によって日の配列を28までか29までか30までか31までかにする必要があります。
具体的には、mat-selectにchangeイベントを追記し、変更イベントを検知するって感じになります。
選択した月によって、月末を28か30か31かにする日のプルダウンにする
選択した月の値を検知し、月末が28か30か31かになるように日のプルダウンの配列を作成します。
まずは、基本的なことですが、月末が28か30か31かになる月は以下の通りです。
1・3・5・7・8・10・12月→31日まで
2月→28日まで※閏年の場合は29日まで
4・6・9・11月→30日まで
mat-selectのselectionChangeイベントを追加して、選択した年を検知できます。
<mat-form-field>
<mat-label>西暦</mat-label>
<mat-select (selectionChange)="changeYear($event)">
<mat-option *ngFor="let year of years" [value]="year">
{{ year }}
</mat-option>
</mat-select>
</mat-form-field>
changeYearをdate-select.component.tsに追加します。
選択した年は、selectedYearという変数に入れて、他の関数内でも使用できるようにしました。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-date-select',
templateUrl: './date-select.component.html',
styleUrls: ['./date-select.component.css']
})
export class DateSelectComponent implements OnInit {
public years: string[] = [];
public monthes: string[] = [];
public days: string[] = [];
public selectedYear: string;
constructor() { }
ngOnInit(): void {
this.yearsList();
this.monthesList();
this.daysList();
}
/**
* 西暦のプルダウンを作成
*
* @return 西暦のプルダウンを今年→1900年になるように配列を作成
*/
public yearsList() {
// 配列を初期化
this.years = [];
// 今日の日付・時刻のオブジェクトを生成
const now = new Date();
// 上記のオブジェクトより年を取得
const year = now.getFullYear();
// 1900年-今年までの配列を作成。i.toString()でiを数値型から文字列型に変換している。
for (let i=1900; i<=year; i++) {
this.years.push(i.toString());
}
return this.years.reverse();
}
/**
* 月の配列を作成
*/
public monthesList() {
this.monthes = [];
for (let i = 1; i <= 12; i++) {
this.monthes.push(i.toString());
}
}
/**
* 日の配列を作成
*/
public daysList() {
this.days = [];
for (let i = 1; i <= 31; i++) {
this.days.push(i.toString());
}
}
/**
* 西暦で選択した値を検知
*
* @param event 選択した年
*/
public changeYear(event: any) {
this.selectedYear = event.value;
}
}
試しに、event.valueをconsole.logでデバッグしてみると良いでしょう。プルダウンで選択した西暦の値をコンソール上で取得できると思います。
西暦と同じ要領で月の値の検知もできるようにchangeイベントを追加しましょう。
<mat-form-field>
<mat-label>西暦</mat-label>
<mat-select (selectionChange)="changeYear($event)">
<mat-option *ngFor="let year of years" [value]="year">
{{ year }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-label>月</mat-label>
<mat-select (selectionChange)="changeMonth($event)">
<mat-option *ngFor="let month of monthes" [value]="month">
{{ month }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-label>日</mat-label>
<mat-select>
<mat-option *ngFor="let day of days" [value]="day">
{{ day }}
</mat-option>
</mat-select>
</mat-form-field>
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-date-select',
templateUrl: './date-select.component.html',
styleUrls: ['./date-select.component.css']
})
export class DateSelectComponent implements OnInit {
public years: string[] = [];
public monthes: string[] = [];
public days: string[] = [];
public selectedYear: string;
public selectedMonth: string;
constructor() { }
...省略...
/**
* 西暦で選択した値を検知
*
* @param event 選択した年
*/
public changeYear(event: any) {
this.selectedYear = event.value;
}
/**
* 月で選択した値を検知
*
* @param event 選択した月
*/
public changeMonth(event: any) {
this.selectedMonth = event.value;
}
}
選択した年と月の値を検知できるようになっているので、検知した値をもとに日の配列を再作成します。
ここでは、ライフサイクルフックのngDoCheckを使いました。ngDoCheckの中で、選択した年と月の値に応じて月末が28や30や31になるようにしています。※一旦閏年は考慮しない。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-date-select',
templateUrl: './date-select.component.html',
styleUrls: ['./date-select.component.css']
})
export class DateSelectComponent implements OnInit {
public years: string[] = [];
public monthes: string[] = [];
public days: string[] = [];
public selectedYear: string;
public selectedMonth: string;
constructor() { }
ngOnInit(): void {
this.yearsList();
this.monthesList();
this.daysList();
}
ngDoCheck(): void {
if (this.selectedYear && this.selectedMonth) {
this.days = [];
// 31日まである月
if (this.selectedMonth === '1' ||
this.selectedMonth === '3' ||
this.selectedMonth === '5' ||
this.selectedMonth === '7' ||
this.selectedMonth === '8' ||
this.selectedMonth === '10' ||
this.selectedMonth === '12') {
for (let i = 1; i <= 31; i++) {
this.days.push(i.toString());
}
}
// 30日まである月
if (this.selectedMonth === '4' ||
this.selectedMonth === '6' ||
this.selectedMonth === '9' ||
this.selectedMonth === '11') {
for (let i = 1; i <= 30; i++) {
this.days.push(i.toString());
}
}
// 2月の場合。一旦閏年は考慮しない
if (this.selectedMonth === '2') {
for (let i = 1; i <= 28; i++) {
this.days.push(i.toString());
}
}
}
}
/**
* 西暦のプルダウンを作成
*
* @return 西暦のプルダウンを今年→1900年になるように配列を作成
*/
public yearsList() {
// 配列を初期化
this.years = [];
// 今日の日付・時刻のオブジェクトを生成
const now = new Date();
// 上記のオブジェクトより年を取得
const year = now.getFullYear();
// 1900年-今年までの配列を作成。i.toString()でiを数値型から文字列型に変換している。
for (let i=1900; i<=year; i++) {
this.years.push(i.toString());
}
return this.years.reverse();
}
/**
* 月の配列を作成
*/
public monthesList() {
this.monthes = [];
for (let i = 1; i <= 12; i++) {
this.monthes.push(i.toString());
}
}
/**
* 日の配列を作成
*/
public daysList() {
this.days = [];
for (let i = 1; i <= 31; i++) {
this.days.push(i.toString());
}
}
/**
* 西暦で選択した値を検知
*
* @param event 選択した年
*/
public changeYear(event: any) {
this.selectedYear = event.value;
}
/**
* 月で選択した値を検知
*
* @param event 選択した月
*/
public changeMonth(event: any) {
this.selectedMonth = event.value;
}
}
これで、例えば2020、2を選択すれば、動的に月末が28になるし、2021、3を選択すれば月末は31に動的に変わります。
閏年を考慮する
まず閏年の定義からです。
- 西暦が4で割り切れること
- 西暦が100で割り切れること。ただし400で割り切れない場合は閏年ではない
上記の条件になります。例えば閏年には、2020、2016、2012、…2000などがあります。
ただ、1900、2100年は4で割り切れて100でも割り切れますが、400で割り切れないため閏年ではないという特殊な例もあります。これらを考慮して、閏年も加えた日の配列を完成させましょう。
閏年の判定ロジック
/**
* 閏年か判定
*
* @return trueは閏年、falseは閏年ではない
*/
public isLeapYear(selectedYear: string) {
// 文字列から数値に変換
const year = Number(selectedYear);
if (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) {
return true;
}
return false;
}
上記の閏年判定のロジックを先ほどのngDoCheckの2月の部分に組み込みます。
ngDoCheck(): void {
if (this.selectedYear && this.selectedMonth) {
this.days = [];
// 31日まである月
if (this.selectedMonth === '1' ||
this.selectedMonth === '3' ||
this.selectedMonth === '5' ||
this.selectedMonth === '7' ||
this.selectedMonth === '8' ||
this.selectedMonth === '10' ||
this.selectedMonth === '12') {
for (let i = 1; i <= 31; i++) {
this.days.push(i.toString());
}
}
// 30日まである月
if (this.selectedMonth === '4' ||
this.selectedMonth === '6' ||
this.selectedMonth === '9' ||
this.selectedMonth === '11') {
for (let i = 1; i <= 30; i++) {
this.days.push(i.toString());
}
}
// 2月の場合
if (this.selectedMonth === '2') {
if (this.isLeapYear(this.selectedYear)) {
for (let i = 1; i <= 29; i++) {
this.days.push(i.toString());
}
} else {
for (let i = 1; i <= 28; i++) {
this.days.push(i.toString());
}
}
}
}
}
休日で空いた時間の暇つぶしを探せるアプリを公開しています。
コメント