(4/30)みんなで学ぶBlazor:コンポーネントとルーティングの紹介

(4/30)みんなで学ぶBlazor:コンポーネントとルーティングの紹介

筆者は当初ASP.NET Core API + Blazor Serverを使用していたため、Blazor Serverを例に説明します。後日Blazor WebAssemblyを研究し終えたら、その知見を追加します。

最終更新 2021/12/10 23:01
StrayaWorker
読了目安 3 分
カテゴリ
Blazor
テーマ
みんなで学ぶBlazorシリーズ
タグ
.NET C# ASP.NET Core Blazor

筆者は当初 ASP.NET Core API + Blazor Server を使用していたため、Blazor Server を例に説明します。後日 Blazor WebAssembly を研究し終えたら、その知見も追記します。

まず、Component は再利用可能なので、Index.razor に二つの Counter を置き、プロジェクトを起動します(完全なデバッグが不要な場合は Ctrl+F5 を押すと、デバッグなしモードで起動し、起動が速くなります。ファイルを保存するたびに Blazor が検出し、Web ページをリロードすると新しいプログラムが読み込まれます)。ブラウザ上の二つの Counter にはそれぞれ独自の「Click me」ボタンがあり、それぞれクリックすると数字が個別に増加します。これは別々の Component であることを示しています。では、これらの数字はどこで定義されているのでしょうか?

Counter.razor を開くと、一番上に @page ディレクティブがありますが、これについては後述します。続いて HTML といくつかの C# コード、最後に @code ブロックがあります。これが Blazor の不思議なところです。@code は一般的な Web ページで JavaScript が行うこと(変数の定義、メソッドの実装、バックエンドや API へのリクエスト送信など)に相当しますが、Blazor では C# で記述します。ここではプライベート変数 currentCount とメソッド IncrementCount() が定義されています。このメソッドを呼び出すのは「Click me」ボタンで、ボタンをクリックするたびに currentCount が +1 され、その結果が <p> 要素内に表示されます。

Index.razor と Counter.razor

二つの独立した Counter

currentCount の定義方法とページ表示は、モデルバインディング(model binding)です。つまり、データとページがバインドされている関係です。.NET Framework の View における @model@Viewbag、Angular の [(ngModel)] も同じ考え方で、データがページに流れ、ページの操作がデータの動作に影響を与えることを実現しています。

別の変数 myClass を定義し、そこに Bootstrap のクラスを代入して、その変数を button の class に配置します。HTML 内で C# のコードを使う場合は必ず @ で始める必要があることを覚えておいてください。そうしないと Blazor がコンパイルできません。ページをリロードすると、ボタンのスタイルが変わっているのがわかります。Blazor が myClass の値 text-primary bg-warning を button の class に挿入してくれています。

Counter ボタンに myClass を追加

次に FetchData.razor を見てみましょう。@using BlazorServer.Data がありますが、これは後で _Import.razor に移動できます。では、@inject WeatherForecastService ForecastService とは何でしょうか?まず @code ブロックを見ると、WeatherForecast[] 型の変数 forecasts が定義され、非同期メソッド OnInitializedAsync 内で ForecastService.GetForecastAsync(DateTime.Now) を呼び出し、その結果を forecasts に代入しています。注意深い人は気づいたでしょうが、一番上の ForecastService@code ブロック内の ForecastService はまったく同じものです。

FetchData.razor

GetForecastAsync() メソッドをクリックして F12 を押すと、このメソッドがランダムに生成された 5 つの天気データの配列を返すことがわかります。HTML 内では forecastsnull でないかチェックし、null でなければ <table> を生成し、その中で foreach を使って forecasts の日付、摂氏、華氏、天気状態を一つずつ表示しています。

Service によるデータ生成とレンダリング

先ほど述べたように、Blazor は単一の Web ページを持ち、その他の内容はすべて Component で構成されています。イベントが発生するたびに、Server または WebAssembly は該当する Component をブラウザにレンダリングします。では、Blazor はどの Component を表示すべきかをどのように知るのでしょうか?

その答えは @page ディレクティブにあります。このディレクティブは従来のルーティングに相当します。Index.razor@page"/" で、これがホームページであることを示しています。Counter.razorFetchData.razor にも対応する @page ディレクティブがあります。1 つのページに複数の @page ディレクティブを設定することもできますが、先頭は必ずスラッシュで始め、ダブルクォーテーションで囲む必要があります。筆者はかつて異なる Component の @page を enum で一元管理しようと考えましたが、現状 Blazor ではそのような方法はサポートされていません。また、2 つの Component が同じ @page を使用すると、コンパイル時にエラーが発生するため、重複ルートが発生した場合の処理を心配する必要はありません。

@page ディレクティブ

では、左側のメニューの Home、Counter、Fetch data の各ページはどこで定義されているのでしょうか?MainLayout.razor を開くと、<NavMenu> 要素があります。さらに NavMenu.razor を開くと、3 つの <NavLink> Component があります。これらの Component は Server によってブラウザが認識できる <a> 要素に変換されるため、DevTools で確認しても <a> 要素しか表示されません。

左側メニュー

左側メニューの HTML での表示(a タグ)1

左側メニューの HTML での表示(a タグ)2

MainLayout.razor に戻ると、@Body ディレクティブがあります。これは他の Component が配置される場所で、一種のプレースホルダーです。さらに App.razor の中には <Found><NotFound> という 2 つの Component があります。名前からわかるように、前者は入力された URL に一致する Component が見つかった場合に使用され、後者は一致する Component が見つからなかった場合に使用されます。どちらも MainLayout を使用していることがわかります。また、異なるページに異なる Layout を適用したい場合は、独自の Layout を定義することもできます。

@Body

ここまで来たところで、Blazor Server の動作を復習しましょう。Angular と同様に、階層的に下っていく構造で、管理が容易です。Blazor WebAssembly と Blazor Server の index.html_Layout.cshtml はほぼ同等であり、appsettings.json ファイルが存在しない点が異なります。通常、プログラムとデータベースの接続に必要な接続文字列はこのファイルに格納されます。このことからも、Blazor WebAssembly は実際には受動的にデータを受け取るだけで、データベースに能動的に接続できないことがわかります。筆者はここで EF Core を参照しようと試みましたが、やはり Blazor WebAssembly からデータベースにアクセスすることはできませんでした。.NET Framework の世界では XML 形式の web.config が使われていましたが、.NET Core では JSON 形式の appsettings.json に変更されています。

参考: ASP NET Core blazor project structure

注:本稿のコードは .NET 6 + Visual Studio 2022 でリファクタリングしています。原文リンクとリファクタリング後のコードを比較して学習することをお勧めします。お読みいただきありがとうございます。原作者をサポートしてください。

さらに探索

関連読書

その他の記事
同じカテゴリ / 同じタグ 2021/12/25

(29/30)みんなで学ぶBlazor:Blazor単体テスト

システム開発において最も退屈なプロセスは、おそらくバグ修正です。特に、null オブジェクトにアクセスしようとするエラー(`Object reference not set to an instance of an object.`)は、多くの初心者が最初に直面する問題です。退屈なバグ修正から解放されるために、この記事では「単体テスト」を紹介します。

続きを読む
同じカテゴリ / 同じタグ 2021/12/25

(28/30)みんなで学ぶBlazor:ポリシーベースの認可

以前に「ASP.NET Core Identity」は「Claim」ベースの検証を使用すると述べましたが、実は「ASP.NET Core Identity」には異なる種類の認可方法があります。最も簡単な「ログイン認可」「ロール認可」「Claim認可」ですが、これらはすべて同じ方法で実現されています:原則認可(ポリシーベースの認可)です。

続きを読む