CodeZine(コードジン)

特集ページ一覧

C#でSPAを実現する、Blazorコンポーネントの開発

ASP.NET Core Blazorチュートリアル 第2回

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2021/01/13 11:00
目次

コンポーネントの作成

 次に、カレンダー機能を実現するコンポーネントを作成しましょう。コンポーネントといっても、特に難しいわけではありません。Blazorのコンポーネントは、入力フォームなどのUI要素を持つRazorページとして作成します。

 ソリューションエクスプローラーのSharedフォルダを選択して、右クリックし、[追加]ー[Razorコンポーネント]を選びます。「新しい項目の追加」というダイアログが表示されますので、「Calendar.razor」という名前にして、追加ボタンをクリックします。

新しい項目の追加
新しい項目の追加

 すると、h3タグと空の@codeディレクティブだけのファイルが作成されます。今回、h3タグは不要ですので削除し、代わりに次のような、カレンダーをテーブルタグで作成するHTMLコードを追加しましょう。なお、SelectDay、NextMonthは、それぞれ後で追加するプロパティとメソッドです。

[リスト3]Calendar.razor
<div class="calendar">
    <table>
        <caption>
            <button class="calendar-day"
              @onclick="@(() => NextMonth(-1))">◀</button>

            @SelectDay.ToString("yyyy年MM月dd日")

            <button class="calendar-day"
              @onclick="@(() => NextMonth(1))">▶</button>
        </caption>
        <thead>
            <tr>
                <th class="calendar-day">日</th>
                <th class="calendar-day">月</th>
                <th class="calendar-day">火</th>
                <th class="calendar-day">水</th>
                <th class="calendar-day">木</th>
                <th class="calendar-day">金</th>
                <th class="calendar-day">土</th>
            </tr>
        </thead>
        <tbody>
        </tbody>
    </table>
</div>
@code {}

 前回説明したように、@onclickディレクティブを使うと、クリック時のイベントハンドラを指定することができます。つまりここでは「button要素をクリックするとNextMonthメソッドが呼ばれる」という意味となります。

 なお、イベントハンドラに引数を渡す場合は、次のようなラムダ式の構文で記述する必要があります。

() => NextMonth(1)]

 単に、@NextMonth(1)のように書きたくなりますが、コンパイルエラーになってしまうので注意しましょう。

1か月分の日付の作成

 今回のカレンダーは、該当の1か月を表示するものです。1か月分の日付を表示する方法は、いろいろ考えられそうですが、今回は、週単位のDateTime型の配列を作成する方法にしてみました。Calendar.razorに、次のようなコードを追加します。

[リスト4]Calendar.razor
~略~
@code {
    // 本日の設定
    public DateTime Today { get; } = DateTime.Now;

    // 1か月分の週毎のDateTime配列を受け取る
    public IEnumerable<DateTime[]> Weeks;

    // 選択されている日
    public DateTime SelectDay { get; set; }

    // 1か月分の週毎のDateTime配列を返す
    public IEnumerable<DateTime[]> GetWeeks()
    {
        // 月初
        DateTime day = new DateTime(SelectDay.Year, SelectDay.Month, 1);
        DateTime end = day.AddMonths(1).AddDays(-1); // 月末(翌月1日の前日)

        var week = new DateTime[7];

        while (day.Month == SelectDay.Month) // 該当の月の間
        {
            week[(int)day.DayOfWeek] = day; // 1週分の配列に設定する②

            // 土曜日または月末のとき ③
            if (day.DayOfWeek == DayOfWeek.Saturday || day.Day == end.Day)
            {
                yield return week;
                Array.Clear(week, 0, week.Length);
            }
            day = day.AddDays(1); // 翌日にする
        }
    }

    protected override void OnInitialized()
    {
        SelectDay = Today;  // 今日を設定する
        Weeks = GetWeeks(); // 当月の週ごとのDateTime配列を取得する
    }
}

 GetWeeksメソッドは、該当月の1日から月末までを、週単位にDateTime型の配列で返すものです。

 DateTimeクラスのDayOfWeekプロパティは、週を示す列挙型定数で、日曜が0、順に土曜が6の数値として取得可能です。コードの②では、そのDayOfWeekプロパティの値をそのまま利用して、配列の添字として指定しています。

 カレンダー表示の1週間は、土曜日または月末で終わりなので、その場合は、yield returnで配列を返すようにしています(③)。

 OnInitializedメソッドは、コンポーネントが初期化されるときに呼び出されるメソッドです。ここでは、本日の日付の設定と、Weeksプロパティに当月分のDateTime配列を取得するコードを追加しています。

日付の設定

 C#のコード部分ができましたので、次に1か月の日付を示すWeeksプロパティを、HTMLに反映させましょう。さきほどのカレンダテーブルのtbodyタグを、次のコードに変更します。

[リスト5]Calendar.razor
~略~
<tbody>
  @foreach (var days in Weeks)
  {
    <tr>
        @foreach (var day in days)
        {
          <td class="calendar-day">
            @if (day != default(DateTime))
            {
               // css用文字列の作成①
               var css = "calendar-day "
                + day switch
                {
                    _ when (day.Date == Today.Date) => "today", 
                    { DayOfWeek: DayOfWeek.Saturday } => "saturday", 
                    { DayOfWeek: DayOfWeek.Sunday } => "sunday",
                    _ => ""
                };
              <button class="@css"
                @onclick="@(() => DayClick(day))">@day.Day</button>
            }
          </td>
        }
      </tr>
  }
</tbody>
~略~

 @foreach、@ifディレクティブは、名前から想像できるとおり、C#の同じ名前のステートメントと同様の機能となります。タグの後の()に、C#のコードが記述できます。ここでは、@foreachディレクティブを二重に使って、Weeksプロパティから、週単位で日ごとのDateTimeオブジェクトを取り出しています。各日付の表示は、button要素で作成しています。

 コード①では、button要素のclass属性に設定するクラス名を、cssという変数に設定しています。このクラス名は、日付(button)のデザインを「本日」「土曜日」「日曜日」で変更するためのものです。「本日」や曜日の判定、クラス名の文字列作成のところは、素直にIF文で書いても問題ありませんが、ここではC# 8.0のswitch式を利用してみました。

 また、button要素には、イベントハンドラ「DayClick」を追加しています。DayClickの引数は、クリックされた日を示すDateTimeオブジェクトとなります。

【note】switch式

 C# 8.0のswitch式では、従来のswitch構文のcaseやbreakキーワードの代わりに、「=>」という記号を使用します。さらに判定する変数は、switchキーワードの前に記述します。またswitch式での条件判定では、パターンマッチングと呼ばれる機能を使い、柔軟に記述できるようになっています。

 今回のコードのswitch式にある、「_(アンダースコア) 」は、「判定する変数がすべての場合に一致する」という意味です。whenキーワードは、フィルター的に条件を追加したい場合に使います。

 { DayOfWeek: DayOfWeek.Saturday } は、プロパティパターンと呼ばれるもので、プロパティと判定したい値を「:(コロン)」でつなぎます。つまり、変数「day」のプロパティDayOfWeekが、DayOfWeek.Saturdayと一致する場合、という意味になります。

 最後の、「_ => ""」は、不要のようにも思えますが、switch式では、すべての条件を網羅する必要があるため追加しています。switch構文でのdefaultラベルと同様の意味となります。

コンポーネントのイベント連携

 次に、カレンダーの表示を、「前月」「来月」に変更する処理を追加しましょう。captionタグ内にあるボタンのクリックで変更できるようにします。すでにbutton要素にはハンドラを指定していますので、ここではイベントハンドラとして呼び出されるNextMonthメソッドを追加します。

 それから、各日付をクリックしたときの処理も追加しておきます。イベントハンドラであるDayClickメソッドを、@codeディレクティブ内に追加します。

[リスト6]Calendar.razor
~略~
    private void NextMonth(int n) => SelectDay = SelectDay.AddMonths(n);

    [Parameter]
    public EventCallback<DateTime> OnClick { get; set; }

    // 日付がクリックされた時に呼ばれる
    private void DayClick(DateTime d)
    {
        SelectDay = d; // クリックされた日を設定する
        OnClick.InvokeAsync(d); // イベントを通知する①
    }
~略~

 NextMonthメソッドは、DateTimeクラスのAddMonthsメソッドを呼び出すだけです。AddMonthsメソッドの引数を負にすれば、過去の日付に設定することができます。

 DayClickメソッドでは、選択された日の設定と共に、イベントを通知するコードを追加しています(①)。これは、コンポーネントで発生したイベントを、そのコンポーネントを呼び出している側にも通知する処理です。

 このようにイベントを連携するには、EventCallback型のプロパティを追加して、InvokeAsyncメソッドを呼び出します。するとこのプロパティを介して、呼び出し側のイベントハンドラが実行されます。なお、このプロパティはコンポーネント外から操作するパラメータとなりますので、[Parameter]を付加して宣言しておきます。

 これでカレンダーコンポーネントの完成です。

コンポーネントの表示

 カレンダーコンポーネントが完成しましたので、index.razorの編集に戻りましょう。カレンダーコンポーネントの表示は、次のように簡単です。

[リスト7]index.razor
<Calendar OnClick="ClickHandler" />
~略~
@code {
    void ClickHandler(DateTime dt){ }
}
~略~

 カレンダーコンポーネントがクリックされると、ハンドラであるClickHandlerメソッドが呼び出されます。ClickHandlerメソッドについては、次回解説します。

カレンダーコンポーネントの表示
カレンダーコンポーネントの表示

 カレンダー用のCSSの設定がない場合は、デフォルトのボタン表示となります。

最後に

 日記の本文を、データベースから読み込む処理/保存する処理は、次回解説することにします。



  • LINEで送る
  • このエントリーをはてなブックマークに追加

バックナンバー

連載:ASP.NET Core Blazorチュートリアル

著者プロフィール

  • WINGSプロジェクト 高江 賢(タカエ ケン)

    <WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂...

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XM...

あなたにオススメ

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5