LoginSignup
1
0

More than 1 year has passed since last update.

【Blazor Serverアプリ】ページからレイアウトへのイベントコールバック ~ @bodyを越えて

Last updated at Posted at 2023-03-31

はじめに

  • この記事では、以下のような読者像を想定しています。
    • Blazor Serverアプリのチュートリアルを済ませた
  • この記事ではツール類の使用方法には言及しません。

この記事で解ること

  • カスケーディングパラメータを使ってイベントコールバックを行う方法について言及します。

環境

  • Windows 11
  • VisualStudio 2022
    • 「ASP.NETとWeb開発」導入済み
  • .NET 7.0

やりたいこと

スクリーンショット 2023-03-30 042525.jpg

  • Blazor Serverアプリの新規作成を行ったときに導入されるテンプレでは、ページの右上にリンク付きの「About」が表示されます。
    • これは、ページ全体のレイアウトを担うMainLayout.razorに記述されています。
MainLayout.razor
@inherits LayoutComponentBase
<PageTitle>BlazorApp</PageTitle>
<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>
    <main>
        <div class="top-row px-4">
            <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
        </div>
        <article class="content px-4">
            @Body
        </article>
    </main>
</div>
  • この部分に「現在のページの見出し」を表示することを考えました。
MainLayout.razor
@inherits LayoutComponentBase
<PageTitle>BlazorApp</PageTitle>
<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>
    <main>
        <div class="top-row px-4">
            <h1>_sectionTitle</h1>
        </div>
        <article class="content px-4">
            @Body
        </article>
    </main>
</div>
@code {
    protected string _sectionTitle = string.Empty;
}
  • この見出しは、@Bodyの中身である@pageコンポーネントから更新される必要があります。
  • これが@Bodyでなく、通常の子要素コンポーネントであれば、以下のようにしてイベントコールバックを使えば済む話です。
MainLayout.razor
    <body OnClickCallback="@((string title) => _sectionTitle = title)" />
  • @Bodyを越えてパラメータを渡すためには、以下のようにカスケーディングパラメータを使うことになります。
MainLayout.razor
<CascadingValue Value="@((string title) => _sectionTitle = title)" Name="Section">
    @Body
</CascadingValue>
  • しかし、カスケーディングパラメータだと、デリゲートを渡すだけでは、コールバックできません。

解決方法

MainLayout.razor
@inherits LayoutComponentBase
<PageTitle>BlazorApp</PageTitle>
<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>
    <main>
        <div class="top-row px-4">
            <h1>@_sectionTitle</h1>
        </div>
        <article class="content px-4">
            <CascadingValue Value="@(EventCallback.Factory.Create<string> (this, (string title) => _sectionTitle = title))" Name="Section">
                @Body
            </CascadingValue>
        </article>
    </main>
</div>
@code {
    protected string _sectionTitle = string.Empty;
}
  • EventCallback.Factoryは、EventCallbackFactoryのインスタンスを保持している静的な読み出し専用フィールドです。
    • Create<TValue>(Object, Action)によって、レシーバとコールバックが生成されます。
  • ここでは、双方ともCascadingValueのパラメータValueに埋め込みましたが、コールバックを@code {~}に書くことも、レシーバをラムダ式でなくメソッドにすることも、もちろん可能です。

ページ側のコード

  • ページ側では、以下のようにしてパラメータを受け取ってコールバックを行います。
Index.razor
@page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />
@code {
    [CascadingParameter(Name = "Section")] protected EventCallback<string> SetSectionTitle { get; set; }
    protected override async Task OnInitializedAsync () => await SetSectionTitle.InvokeAsync ("Home");
}
  • なお、asyncawaitは必須ではなく、この例のように待機が不要であればOnInitialized ()を使うことも可能です。

おわりに

スクリーンショット 2023-03-31 041332.jpg

  • この結論に至るまでに、StateHasChanged ()を使って強制的に再描画させたり、DIコンテナにScopedなサービスを登録したりなどと、試行錯誤を重ねました。
    • 同様の課題を抱えた方のお役に立てば幸いです。
  • 執筆者は、Blazor、ASP.NETともに初学者ですので、誤りもあるかと思います。
    • お気づきの際は、是非コメントや編集リクエストにてご指摘ください。
    • あるいは、「それでも解らない」、「自分はこう捉えている」などといった、ご意見、ご感想も歓迎いたします。
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0