Blazor は JavaScript を必ずしも必要としませんが、既存の JS ライブラリの中には非常に便利なものもあり、JavaScript を使いたくないからといってすべて捨てるのはもったいないです。Blazor には JavaScript を呼び出す方法が用意されており、これを JavaScript interoperability(略して JavaScript interop)と呼びます。この記事では、Delete ボタンに確認ダイアログを実装します。削除は重要な機能なので、ユーザーが軽い気持ちで簡単に削除できないようにするためです。
Day 07 で説明したように、Blazor には組み込みの JavaScript Service があるため、Program.cs で依存関係の注入を行う必要はなく、使いたいページで注入するだけで済みます。razor コンポーネントに注入する場合は、@inject IJSRuntime js と記述するだけです。

次に、元のReturnPostId()をDeletePost()にリネームし、型をTaskに変更し、内部のロジックを少し修正します。JavaScript もEventCallback の方式で呼び出されており、値を返すInvokeAsyncと値を返さないInvokeVoidAsyncが利用できます。第1引数にはJSメソッドの名前を指定し、そのJSメソッドに引数が必要な場合は順に後ろに並べるだけです。

では、このコードをカプセル化して C# クラスを作り、強タイピングの利点を享受したい場合はどうすればよいでしょうか?Blazor にはその方法も用意されています。まずSharedフォルダにJsInteropClasses.csを作成します。このクラスでは3つのことだけを行います。依存関係の注入、confirm メソッド、そしてDisposeです。

次にPostBase.razor.csの先頭で、JS をJsInteropClassesに注入し、IDisposableを継承します。

そして、元のメソッドを先ほどカプセル化した_jsClass.Confirm()メソッドに変更し、最後にDisposeメソッドをオーバーライドします。

これで、元のPostBase.razor.csで直接 JS のconfirmを呼び出したのと同じ結果になるのがわかります。忘れていましたが、Post.razorで Delete ボタンのクリックイベントのコールバックメソッドをDeletePostに変更しておく必要があります。


Blazor で JS ファイルを読み込める場所は4つあります。それぞれ<head>、<body>、外部JS script、そして Blazor 起動後の読み込みです。いずれの場合も、必要な<script>を_Layout.cshtml(Blazor Server)またはindex.html(Blazor WebAssembly)に配置します。特に最後の方法は特殊なので、ここで説明します。
_Layout.cshtmlの元の_framework/blazor.server.jsの下に独自の script を追加します。中身は非常にシンプルで、console.log で文字を出力するだけです。特に注意しなければならないのは、元の script に属性autostart="false"を追加することです。これは Blazor にプログラムを自動起動しないように指示するもので、この属性を追加しないと、次のようなエラーメッセージが表示されます。


しかし、現在の削除確認ダイアログはあまり見栄えが良くないので、別のライブラリに切り替えましょう。筆者は多くの人がSweetAlert2を使っているのを見たので、試してみることにしました。
まず SweetAlert2 の公式サイトからファイルをダウンロードし、sweetalert.jsを<head>に配置します。次に、先ほど追加した4番目のBlazor.start()を削除します。また、_framework/blazor.server.jsのautostart="false"も忘れずに削除してください。あるいは、Blazor.start()内に直接記述することもできます。



さらに独自の<script>を追加します。ここでは特別にPromiseを使用しています。Promise は非同期処理をより扱いやすくするための構文で、resolveは処理が成功した場合に返す内容です(ここでの成功は例外が発生しなかったという意味で、つまり「確定」でも「キャンセル」でも押された場合に値を返します)。

Confirmメソッドは、先ほど定義したSweetConfirm()を呼び出すように変更します。

Delete ボタンをクリックし、「確定」を押すと、willDeleteが true になり、resolveがそのwillDeleteを返します。JsInteropClassesのConfirmが無事にtrueを受け取ります(ここに少し問題があります。確定をクリックした後、最初に「削除成功」のポップアップが表示され、その後にJsInteropClassesがtrueを受け取っています。後日、最適化する機会があれば行います)。

「キャンセル」をクリックすると、willDeleteは null になります。

Promise はフロントエンドで避けて通れない構文です。興味のある方は、筆者が添付したリンクをご覧ください。筆者は当初 Observable と subscribe の構文を使おうと考えましたが、SweetAlert2 ではまだ実装されていません。
また、SweetAlert2 には既に Blazor 版 も用意されており、方法はほぼ同じです。どうしても JS に触れたくない方は試してみてください。
最後に、この記事の動画効果で締めくくります。

参照:
- Blazor JavaScript interoperability (JS interop)
- Call JavaScript functions from .NET methods in ASP.NET Core Blazor
- HANDLING BUTTONS (CONFIRM, DENY, CANCEL)
- Day 20:JavaScript interop
- JavaScript Promise 全介绍
- Support ObservableLike #1982
- Blazor - Making a Promise in JS - SweetAlert2 Confirmation Dialog
- SweetAlert - upgrading-from-1x
注:本記事のコードは .NET 6 + Visual Studio 2022 でリファクタリングされ、SweetAlert は 2.0 バージョンを使用しています。元の記事とはかなり異なりますので、元の記事のリンクとリファクタリング後のコードを比較して学習してください。お読みいただきありがとうございます。