副業におすすめなサイトを見る→

【Angular】mat-selectで閏年にも対応した日付のプルダウンを実装する

プルダウン

アプリリリースのお知らせ

予定を探すアプリyoteipickerをリリースしました。

アプリの利用用途:暇だから何か予定入れたいけど今週の土日は何しようかな〜?ってときに使えるアプリです。

>>ココナラと似てるおすすめの副業サイトを確認する

>>リモートワークもあるおすすめの転職サイトを確認する

休日で空いた時間の暇つぶしを探せるアプリを公開しています。

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>
Contents

西暦のプルダウンを作成する

西暦のプルダウンを作成しましょう。

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日まで

31日まである月の覚え方

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());
          }
        }
      }
    }
  }

>>ココナラと似てるおすすめの副業サイトを確認する

>>リモートワークもあるおすすめの転職サイトを確認する

休日で空いた時間の暇つぶしを探せるアプリを公開しています。

スキルを売り買いするならココナラ

コメント

コメントする

CAPTCHA


Contents
閉じる