【Blazor入門】Blazor初心者がログインからチャット機能まで付けてデプロイしてみた ～その1～

##はじめに
`Blazor`がリリースされてから1年程経ったのでしょうか。
ボチボチとドキュメントとか記事とかが上がってきているみたいなので重い腰をあげてやってみることにしました。

`Blazor`やってみたいけどどうすりゃいいんだろ、って人多いと思う(?)のでその手助けとなる記事になればと。

##環境
-  VS2019
-  .NET Core SDK 3.1


##情報収集
とりあえず色々とよくわかんないので[Blazor Advent Calendar 2019](https://qiita.com/advent-calendar/2019/blazor)を一通り読んでみました。

分かった事としてはこう。

###Razor記法
`MVC時代`に`razor記法`という、`HTML`と`C#`を悪魔合体させられる記述が可能となった。`cshtml`という拡張子。

例えば、こんな感じ。

```C#:hoge.cshtml
@for (int i = 0; i < 3; i++) {
  <p>for文のループの「@i」回目です。</p>
}
```
`C#`の構文と組み合わせる事で同じような記述を排除できるスグレモノ。
ただ、クライアントの処理は当然、`JavaScript`で書く必要があった。まーしょうがないよね。

###Blazor
そんな時に現れたのが`Blazor`。
コイツはクライアント処理まで`C#`で記述することができます。`razor`という拡張子。

```C#:counter.razor
@page "/counter"

<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
```
![counter.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172223/7c9789d3-bd03-c06f-24b3-da501bcf527c.gif)

`フルC#`により処理が完結させられるのは興奮しました。

これをみればある程度は分かる、かも。
[![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172223/4c8b3c6d-14ca-363b-177b-fb3db445901f.jpeg)](https://www.slideshare.net/jsakamoto/c-single-page-web-blazor-148130367)

###懸念
`.razorファイル`に`UI`やらロジックやらを書いていけばいいのはわかった。十分に理解できた。ただ気になった事として、`.razorファイル`がめちゃめちゃfatになりませんか？ってのが非常に気になりました。

でも大丈夫っぽさそうで、
[Blazorでコードビハインドでロジックとビューを分離して記述する](https://qiita.com/nobu17/items/b7dc78db7beb1d833dc8)に解決策が書いてあり、どうやらちゃんと分けて書くことができるようで安心しました。

今回は`Partial Class`でコードを書きます。`ViewModel`を使ってもよかったですが、`DI`をわざわざ書いてやる必要があったのでパス。

##最終成果物
あとでGIFとソースコードを載せます。

##アプリケーションの作成
さて、これから実際に作っていきます。

###画面遷移図
特に要らないとも思いましたが、やったことないことをちゃんとやっていこうと思ったので簡易的ですが、[LucidChart](https://app.lucidchart.com/)というサイトで作ってみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172223/868ece5e-2df0-3024-9870-f0d054806524.png)

###環境の構築
私の場合は色々と環境が整っていたのでやったのは`.NET Core 3.1のSDKをインストール`するぐらい。
[Download .NET Core 3.1](https://dotnet.microsoft.com/download/dotnet-core/3.1)

`Visual Studio`が入ってないとか`Blazor`のテンプレートがない場合は以下の記事を参考にするとよいと思います。
[C#でSPAを作るBlazorの、開発環境の作り方 - Visual Studio編](https://qiita.com/jsakamoto/items/4f3668e76eca6fca6b7b)

###アプリケーションの作成
[Blazor をお勧めできる人は誰か?](https://qiita.com/jsakamoto/items/52c5ef52fc5ccebaae1f)を読んだんですが、サーバーとクライアントのどちらをどういう場合に使い分けるのかがよく分からなかったので取り急ぎはサーバーの方で作成します。

サーバー兼クライアントとして動かせるのでまーよいでしょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172223/0361f259-ec2e-bfc8-8464-b9a5ebabd8d5.png)

####実行
↓↓↓↓↓↓↓ あなたの記事の内容
とりあえず実行してみて動くっていうのは初学者からすると物凄くハードルが下がるので非常にありがたいです。ということで作りたてほやほやのソリューションをデバッグしてみるとこうなります。
───────
とりあえず実行してみて動くっていうのは初学者からすると物凄く分かりやすいので非常にありがたいです。ということで作りたてほやほやのソリューションをデバッグしてみるとこうなります。
↑↑↑↑↑↑↑ 編集リクエストの内容
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172223/d9e08ddf-1156-0c42-ebdd-365b66441030.png)

色合いがすげーイイ感じです。
では早速、このイイ感じの画面をぶっ壊していきます。

####ログイン画面の生成
ログイン画面を生成していきましょう。

####MainLayout.razorの修正

`MainLayout.razor`を`Body`だけにします。
左のナビゲーションからページ遷移をさせたくないので。

```HTML:MainLayout.razor
@inherits LayoutComponentBase

<div class="main">
    <div class="content px-4">
        @Body
    </div>
</div>
```

####Indexページの修正
インデックスページをログイン画面として使います。

当初は、こういう書き方をしていましたが、

```HTML:Index.razor
<table style="border:none">
    <tr>
        <th>
            <p>ユーザID</p>
        </th>
        <td>
            <input id="txtUserID" type="text" size="24" @bind="@UserID">
        </td>
    </tr>
    <tr>
        <th>
            <p>パスワード</p>
        </th>
        <td>
            <input id="txtPassword" type="password" size="24" @bind="@Password">
        </td>
    </tr>
    <tr>
        <td colspan="2" align="center">
            <input type="submit" value="ログイン">
            <button class="btn btn-primary" @onclick="Login">ログイン</button>
        </td>
    </tr>
</table>
```
調べてみると`EditFormタグ`を使えば`Validation`をイイ感じにやってくれることが分かったので以下の様に変更しました。

```HTML:Index.razor
@page "/"
@* 本当はこのusingディレクティブが無くても動いてくれるけど、using追加しろと言われ続けてしまうのでしょうがなく追加 *@
@using LoginTest.Models

<div align="center">
    <EditForm Model="@LoginData" OnValidSubmit="@OnValidSubmit" OnInvalidSubmit="@OnInvalidSubmit">
        <DataAnnotationsValidator />
        @* ValidationSummaryを配置した箇所に全てのValidationメッセージが表示される *@
        @*<ValidationSummary />*@

        <div class="form-group">
            <label>ユーザーID</label>
            <InputText id="txtUserID" @bind-Value="LoginData.UserID" />
            <ValidationMessage For="@(() => LoginData.UserID)" />
        </div>

        <div>
            <label>パスワード</label>
            <InputText id="txtPassword" type="password" @bind-Value="LoginData.Password" />
            <ValidationMessage For="@(() => LoginData.Password)" />
        </div>

        <button type="submit" class="btn btn-primary">ログイン</button>
    </EditForm>
</div>
```
`EditForm`については以下の記事を参考にさせていただきました。

- [Blazorにおけるフォームバリデーション手法のまとめ](https://qiita.com/nobu17/items/ca17f46abaa3f980c00b)
- [BlazorでSPAするぞ！(8) - Validation -正式版対応済](https://ryuichi111std.hatenablog.com/entry/2020/05/26/040032)

[Blazorでコードビハインドでロジックとビューを分離して記述する](https://qiita.com/nobu17/items/b7dc78db7beb1d833dc8)にも書いてありますが、`razorファイル`に対応する`csファイル`を作成することでコードビハインドとして扱ってくれます。今回で言うと、`Index.razor.csファイル`を`Pagesフォルダ`直下に作成してみてください。

すると、勝手に紐づけてくれます。しゅごい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172223/3567fae2-0dee-c394-1246-708e1495efbb.png)

コードビハインドはこうなります。

```C#:Index.razor.cs
using System;
using LoginTest.Models;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;

namespace LoginTest.Pages
{
    public partial class Index
    {
        #region プロパティ
        /// <summary>
        /// Inject属性を指定することで、NavigationManagerのサービスの依存関係を挿入します。
        /// </summary>
        [Inject]
        public NavigationManager Navigation { get; set; }
        /// <summary>
        /// ログイン情報を保持
        /// </summary>
        public LoginData LoginData { get; set; }
        #endregion

        #region コンストラクタ
        public Index()
        {
            //Index.razorから参照するのでインスタンス生成をしておかないとエラーとなる
            LoginData = new LoginData();
        }
        #endregion

        #region メソッド
        /// <summary>
        /// Validate処理成功時に処理
        /// </summary>
        /// <param name="context"></param>
        public void OnValidSubmit(EditContext context)
        {
            Console.WriteLine($"OnValidSubmit()");
            Navigation.NavigateTo("Chat", false);
        }
        /// <summary>
        /// Validate処理失敗時に処理
        /// </summary>
        /// <param name="context"></param>
        public void OnInvalidSubmit(EditContext context)
        {
            Console.WriteLine($"OnInvalidSubmit()");
        }
        #endregion
    }
}
```

#####ハマりの共有
少しハマってしまったことが2点あります。
1点目に、`Navigation.NavigateToメソッド`で遷移するにあたって、`NavigationManager`を、`Inject`する必要があります。
遷移先のページを保持している情報を取得しなければいけないということですね。

---
2点目に、`OnSubmitイベント`と`OnValidSubmit/OnInvalidSubmitイベント`は併用不可であり同時に定義すると実行時エラーが発生してしまいます。

念の為に書いておくと、

- **OnSubmitイベント** … Submitボタン押下時に必ず発生する
- **OnValidSubmit/OnInvalidSubmitイベント** … Submitボタン押下時、Validationが成功すればOnValidSubmit、失敗すればOnInvalidSubmitが発生する

実用的な使い勝手としては断然、`OnValidSubmit/OnInvalidSubmitイベント`でしょうね。でも併用できても良いと思うんだけどなー。

---

####Modelの作成
ここまでくれば大体ログイン画面はできました。
`Modelsフォルダ`とその中に`LoginData.cs`を作成してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172223/69db5c7e-fe66-3676-bba5-df51e01732c7.png)

`LoginData.cs`を以下の様なモデルを作成してやればある程度完成です！

```C#:LoginData.cs
using System.ComponentModel.DataAnnotations;

namespace LoginTest.Models
{
    public class LoginData
    {
        [Required(ErrorMessage = "ユーザIDを入力してください。")]
        [StringLength(16, ErrorMessage = "ユーザIDが長すぎます。")]
        public string UserID { get; set; }

        [Required(ErrorMessage = "パスワードを入力してください。")]
        [StringLength(32, ErrorMessage = "パスワードが長すぎます。")]
        public string Password { get; set; }
    }
}
```

####動作確認
チャットページに遷移できるように、`Pagesフォルダ`以下に`Chat.razorファイル`を追加することで以下のような動きになります。

最低限。`@page "/Chat"`だけ記述されていれば遷移出来るはずです。
![Counter.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172223/af0f6111-bec4-1561-7690-992b725f66e9.gif)

テキストボックスへ未入力時にアノテーションが効いててイイ感じです。
ただ今の状態だと、何かしらの値がIDとパスワードに入ってさえいればログインできてしまうのでこの点は後で実装します。

##まとめ
`Blazor`だとクライアント処理も`C#`で書ける反面、全ての処理を`razorファイル`に記述しないといけないと思っていましたが、ちゃんとそこら辺を考えてあって分離できるのはよい機構だなと思いました。

次の記事では、遷移先のチャットページを作成してみたいと思います。

**参考にさせて頂いたページ**

- [Download .NET Core 3.1](https://dotnet.microsoft.com/download/dotnet-core/3.1)
- [C#でSPAを作るBlazorの、開発環境の作り方 - Visual Studio編](https://qiita.com/jsakamoto/items/4f3668e76eca6fca6b7b)
- [Blazor をお勧めできる人は誰か?](https://qiita.com/jsakamoto/items/52c5ef52fc5ccebaae1f)
- [Blazor Advent Calendar 2019](https://qiita.com/advent-calendar/2019/blazor)
- [Blazorでコードビハインドでロジックとビューを分離して記述する](https://qiita.com/nobu17/items/b7dc78db7beb1d833dc8)
- [Blazorアプリケーションでボタンやリンクのクリックの処理で別のページに遷移する](https://www.ipentec.com/document/csharp-blazor-application-navigate-page)
- [Blazorにおけるフォームバリデーション手法のまとめ](https://qiita.com/nobu17/items/ca17f46abaa3f980c00b)
- [BlazorでSPAするぞ！(8) - Validation -正式版対応済](https://ryuichi111std.hatenablog.com/entry/2020/05/26/040032)
- [Using EditForm | Part - 29 | Using ASP.Net Blazor for Absolute Beginners](https://www.youtube.com/watch?v=RHqWGivRZgQ)