14
9

More than 3 years have passed since last update.

[Blazor]コンポーネント間でのステータスのやり取り方法

Posted at

悩んだこと

  • アラート系のメッセージ処理を各ページでゴリゴリ書くのはイケてない
  • 共通化して使いまわししたい
  • componentを作って使いまわすのにも限度があるし、MainLayout.razorで一括に処理できればいいよね
  • 親コンポーネントと子コンポーネント間のやりとりってどうやるの?

今回の解決策

State Containerを作成し、それを介すことで親子間のStatusのやり取りを行いました。Fluxの考え方に似た方法です。

できたもの

IStateMessageService.cs
interface IStateMessageService
{
    string StateMessages { get; }
    event Action OnChange;
    void ClearStateMessages();
    void SetStateMessages(string statusMessages);
}
StateMessageService.cs
public class StateMessageService : IStateMessageService
{
    public string StateMessages { get; private set; }
    public event Action OnChange;

    public void SetStateMessages(IStatusMessages statusMessages)
    {
        StateMessages = statusMessages;
        NotifyStateChanged();
    }

    public void ClearStateMessages()
    {
        StateMessages = null;
        NotifyStateChanged();
    }

    private void NotifyStateChanged() => OnChange?.Invoke();
}
Startup.cs
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // 略
        services.AddScoped<IStateMessageService, StateMessageService>(); // 追記
        // 略
    }
}
MainLayout.razor
@inject IStateMessageService StateMessage
@implements IDisposable

<div class="sidebar">
    <NavMenu />
</div>

<div class="main">
    <AlertMessage Message="StateMessage.StateMessages" />
    <div class="content px-4 vh-100">
        @Body
    </div>
</div>

@code
{
    protected override void OnInitialized()
    {
        StateMessage.OnChange += StateHasChanged;
    }

    public void Dispose()
    {
        StateMessage.OnChange -= StateHasChanged;
    }
}
AlertMessage.razor
@inject IStateMessageService StateMessage
@if(!string.IsNullOrEmpty(Message))
{
    <div class="alert alert-success alert-dismissible @(IsVisible ? "show " : "")text-left shadow" role="alert">
        <button type="button" class="close" data-dismiss="alert" aria-label="Close" @onclick="OnClickCloseButton">
            <span aria-hidden="true">&times;</span>
        </button>
        <p class="m-0">
            @Message
        </p>
    </div>
}

@code {
    [Parameter]
    public string Message { get; set; } = string.Empty;
    public bool IsVisible { get; set; } = true;

    private void OnClickCloseButton()
    {
        IsVisible = false;
        Task.Run(() => StateMessage.ClearStateMessages());
    }
}
ChildComponent.razor
@inject IStateMessageService StateMessage
<button type="button" @onclick="OnClickButton">Show</button>

@code {
    public void OnClickButton()
    {
        StateMessage.SetStateMessages("Hello world!");
    }
}

解説

StateMessageService

DIして各コンポーネントで使っていくサービスです。StateMessageService.SetStateMessagesを使い、表示するメッセージを追加します。内部ではメッセージの追加以外に、StateMessageService.OnChangeに登録されているActionを実行しています。

後々出てきますが、このActionStateHasChangedを追加することで、Messageの内容が変わるたびにDOM更新が走るといった算段です。

Razor Component

フロント側では@injectを使いIStateMessageServiceを受けます。MainLayout.razorにメッセージ表示用のAlertMessageを配置することでどの子コンポーネントでStateMessageService.SetStateMessagesを実行してもメッセージが表示されるようにします。
AlertMessage自体はBootstrapのAlertをラップしているコンポーネントです。Messageに表示したいメッセージを渡せばいい感じに表示してくれます。

AlertMessage.razorではクローズボタンが押された時の処理として、OnClickCloseButtonを定義しています。ここでStateMessageService.ClearStateMessagesを実行することで、非表示及びステートの初期化を行っています。

いいところ

ページ遷移をしてもクローズボタンを押下されない限り表示され続けます。使いどころとしてはブラウザ上でのプッシュ通知などでしょうか? 私個人はAPIのレスポンスメッセージを表示させたりしています。
WASMでもServerSideでもどっちでも使えるところも良いですね。
javascriptを1つも書かずにここまでできるBlazorは本当にすごいと思います。革新的ですね。

悪いところ

紹介したコードではメッセージが1つだけしか表示できません。トーストのようにスタックすることができないので、そういった用途には向かないでしょう。

参考

3 Ways to Communicate Between Components in Blazor
https://chrissainty.com/3-ways-to-communicate-between-components-in-blazor/

環境

  • Visual Studio 2019 ver 16.4.2
  • TargetFramework > netstandard2.0
  • LangVersion > 7.3
  • RazorLangVersion > 3.0
14
9
1

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
14
9