はじめに
この記事では、MS公式のチュートリアルにあるToDoリスト作成を題材にしながら、次の3つをまとめて理解することを目的にします。
- UIイベント
- コールバック
- データバインディング
Blazorの「書き味」がわかる最小例
たとえば、BlazorではHTMLの中でそのままC#を使えます。
@if (currentCount > 3)
{
<p>You win!</p>
}
<ul>
@foreach (var item in items)
{
<li>@item.Name</li>
}
</ul>
このあたりが、Blazorの最初の分かりやすい特徴です。
テンプレートの中にロジックを書けるので、UIと状態のつながりが見えやすいです。
UIイベントは @onclick で受け取る
ボタン押下などのイベントは、@onclick で処理できます。
※JSでよく使うon〜系が使えるイメージ
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
<button class="btn btn-primary" @onclick="() => currentCount++">Click me</button>
ポイントは次の2つです。
- メソッド名を渡す書き方
- ラムダ式でその場に処理を書く書き方
小さい処理ならラムダ式でも十分ですが、処理が増えるならメソッドに分けた方が読みやすくなります。
データバインディングは @bind でつなぐ
Blazorでは、入力欄とC#の変数を @bind で結びつけられます。
<input @bind="text" />
<button @onclick="() => text = string.Empty">Clear</button>
<p>@text</p>
@code {
string text = "";
}
これで、入力欄に入れた内容が text に反映され、<p>@text</p> の表示も連動して変わります。
つまり @bind は、
- 画面の入力
- C#の状態
- 画面への再表示
をつなぐ役割を持っています。
@ が付く記法は Razor ディレクティブ
Blazorでは @if や @foreach、@onclick、@bind など、@ 付きの記法がたくさん出てきます。
最初は細かく分類しなくても大丈夫ですが、
-
@が付くことでRazorとして解釈される - HTMLだけでなくC#の文脈も扱える
くらいで捉えると理解しやすいです。
対話型にするには @rendermode を指定する
Blazor Web Appでは、コンポーネントを対話型にするために @rendermode を指定します。
今回のToDoリストでは、次のように InteractiveServer を使います。
@page "/todo"
@rendermode InteractiveServer
InteractiveServer と InteractiveWebAssembly の違い
InteractiveServer
InteractiveServer では、ブラウザとの WebSocket 接続 を通じてUIイベントをサーバーへ送り、サーバー側で処理した結果をDOM更新として返します。
つまり、実行の中心はサーバー側です。
InteractiveWebAssembly
一方、InteractiveWebAssembly では、コンポーネントコードがブラウザにダウンロードされ、WebAssemblyベースの .NET ランタイム上でクライアント側実行されます
ざっくり整理すると、こうです。
| 項目 | InteractiveServer | InteractiveWebAssembly |
|---|---|---|
| 主な実行場所 | サーバー | ブラウザ |
| 通信 | WebSocketでイベント送受信 | 初回にコードをダウンロード |
| 向いている理解 | サーバー中心の対話型UI | クライアント中心のSPA的な体験 |
ToDoリストの完成コード
最終コードは次の内容です。
@page "/todo"
@rendermode InteractiveServer
<h3>Todo (@todos.Count(todo => !todo.IsDone))</h3>
<input @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>
<ul>
@foreach (var todo in todos)
{
<li>
<input type="checkbox" @bind="todo.IsDone" />
<input @bind="todo.Title" />
</li>
}
</ul>
@code {
private List<TodoItem> todos = new();
string newTodo = "";
void AddTodo()
{
if (!string.IsNullOrWhiteSpace(newTodo))
{
todos.Add(new TodoItem { Title = newTodo });
newTodo = string.Empty;
}
}
}
このコードで起きていることを分解する
1. ページ定義
@page "/todo"
/todo でアクセスできるページとして登録しています。
2. 対話型モードの指定
@rendermode InteractiveServer
この指定によって、このコンポーネントは対話型で動作します。
今回のサンプルではサーバー側でイベントを処理する構成です。
3. 未完了件数の表示
<h3>Todo (@todos.Count(todo => !todo.IsDone))</h3>
ここでは、完了していないToDoだけを数えています。
todo => !todo.IsDone は、「未完了のものだけ数える」という条件です。
4. 入力欄と newTodo のバインド
<input @bind="newTodo" />
入力欄に打ち込んだ文字が、newTodo にそのまま入ります。
5. ボタン押下で AddTodo を呼ぶ
<button @onclick="AddTodo">Add todo</button>
ボタンが押されたら AddTodo を実行します。
これがイベント処理の入り口です。
6. foreach でToDo一覧を描画する
<ul>
@foreach (var todo in todos)
{
<li>
<input type="checkbox" @bind="todo.IsDone" />
<input @bind="todo.Title" />
</li>
}
</ul>
つまり、
-
todosの要素数が増えれば表示も増える -
todo.IsDoneをチェックすれば見た目だけでなく状態も変わる -
todo.Titleを編集すればオブジェクトの値も変わる
というふうに、状態とUIがかなり直結しています。
7. AddTodo の役割
void AddTodo()
{
if (!string.IsNullOrWhiteSpace(newTodo))
{
todos.Add(new TodoItem { Title = newTodo });
newTodo = string.Empty;
}
}
このメソッドのやっていること自体は単純です。
- 入力が空白だけでないことを確認する
-
todosに新しい項目を追加する - 入力欄を空に戻す
この実装で、状態を変えたらUIも変わる コードになります。
todos.Add(...) でリストが増えれば、その結果として foreach の表示も増えます。
このサンプルで理解できる本質
このToDoリストは小さいですが、Blazorの基本がかなり詰まっていました。
1. UIイベント
ユーザー操作を @onclick などで受け取る。
2. コールバック
イベント発生時に AddTodo のような処理を呼ぶ。
3. データバインディング
@bind で入力欄やチェック状態をC#の値と結びつける。
4. 再描画
状態が変わると、その状態に応じてUI表示も更新される。
つまりBlazorは、
「ユーザー操作 → C#の状態変更 → UI更新」
という流れを、かなり自然に書けるフレームワークだと捉えると理解しやすいです。
触ってみて感じたこと
乱暴に言うと、ReactのJS部分を全部C#で簡易に書ける印象でした。
C#を主軸にしながらインタラクティブなUIを組めるのは、.NETに慣れている人ほど面白く感じやすいと思います。
特に最初は、
- HTMLの中でC#を書ける
- イベント処理を書ける
-
@bindで値がつながる
この3つを体感すると、Blazor悪くないと思えてきます。
まとめ
ToDoリストのサンプルを通して見ると、Blazorの基本は次のように整理できます。
-
@onclickでイベントを受け取る -
AddTodoのようなメソッドで処理する -
@bindで入力や状態を同期する -
@foreachで状態から一覧表示を組み立てる -
@rendermodeで対話型モードを選ぶ
最初は「Razorの記法が独特だな」と感じるかもしれませんが、構造としてはかなり素直です。
むしろ、UIと状態のつながりを追いやすい のがBlazorの良さだと思います。
次に進むなら、
-
TodoItemクラスをちゃんと定義する - 削除機能を付ける
- 完了・未完了のフィルタを入れる
- 永続化してページ再読み込み後も残るようにする
あたりを追加すると、理解が一段深まります。