悩んだこと
- アラート系のメッセージ処理を各ページでゴリゴリ書くのはイケてない
- 共通化して使いまわししたい
component
を作って使いまわすのにも限度があるし、MainLayout.razor
で一括に処理できればいいよね- 親コンポーネントと子コンポーネント間のやりとりってどうやるの?
今回の解決策
State Container
を作成し、それを介すことで親子間のStatus
のやり取りを行いました。Fluxの考え方に似た方法です。
できたもの
interfaceIStateMessageService{stringStateMessages{get;}eventActionOnChange;voidClearStateMessages();voidSetStateMessages(stringstatusMessages);}
publicclassStateMessageService:IStateMessageService{publicstringStateMessages{get;privateset;}publiceventActionOnChange;publicvoidSetStateMessages(IStatusMessagesstatusMessages){StateMessages=statusMessages;NotifyStateChanged();}publicvoidClearStateMessages(){StateMessages=null;NotifyStateChanged();}privatevoidNotifyStateChanged()=>OnChange?.Invoke();}
publicclassStartup{publicvoidConfigureServices(IServiceCollectionservices){// 略services.AddScoped<IStateMessageService,StateMessageService>();// 追記// 略}}
@injectIStateMessageServiceStateMessage@implementsIDisposable<divclass="sidebar">
<NavMenu/></div><divclass="main">
<AlertMessageMessage="StateMessage.StateMessages"/><divclass="contentpx-4vh-100">
@Body</div></div>@code{protectedoverridevoidOnInitialized(){StateMessage.OnChange+=StateHasChanged;}publicvoidDispose(){StateMessage.OnChange-=StateHasChanged;}}
@injectIStateMessageServiceStateMessage@if(!string.IsNullOrEmpty(Message)){<divclass="alertalert-successalert-dismissible@(IsVisible?"show ":"")text-leftshadow" role="alert">
<buttontype="button"class="close" data-dismiss="alert" aria-label="Close" @onclick="OnClickCloseButton">
<spanaria-hidden="true">×</span></button><pclass="m-0">
@Message</p></div>}@code{[Parameter]publicstringMessage{get;set;}=string.Empty;publicboolIsVisible{get;set;}=true;privatevoidOnClickCloseButton(){IsVisible=false;Task.Run(()=>StateMessage.ClearStateMessages());}}
@injectIStateMessageServiceStateMessage<buttontype="button"@onclick="OnClickButton">Show</button>@code{publicvoidOnClickButton(){StateMessage.SetStateMessages("Hello world!");}}
解説
StateMessageService
DIして各コンポーネントで使っていくサービスです。StateMessageService.SetStateMessages
を使い、表示するメッセージを追加します。内部ではメッセージの追加以外に、StateMessageService.OnChange
に登録されているAction
を実行しています。
後々出てきますが、このAction
にStateHasChanged
を追加することで、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