ASP.NET Core Blazor で JavaScript 関数から .NET メソッドを呼び出す
注意
これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
警告
このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、 .NET および .NET Core サポート ポリシーを参照してください。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
重要
この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。
現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
この記事では、JavaScript (JS) から .NET メソッドを呼び出す方法について説明します。
.NET から JS 関数を呼び出す方法については、「ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出す」をご覧ください。
静的 .NET メソッドの呼び出し
JavaScript (JS) から静的 .NET メソッドを呼び出すには、JS 関数を使用します。
DotNet.invokeMethodAsync
(推奨): サーバ側コンポーネントもクライアント側コンポーネントも非同期。DotNet.invokeMethod
: クライアント側コンポーネントのみ同期。
メソッドを含むアセンブリの名前、静的 .NET メソッドの識別子、および引数を渡します。
次に例を示します。
{ASSEMBLY NAME}
プレースホルダーは、アプリのアセンブリ名です。{.NET METHOD ID}
プレースホルダーは .NET メソッド識別子です。{ARGUMENTS}
プレースホルダーは、メソッドに渡す省略可能なコンマ区切りの引数であり、各引数は JSON シリアル化可能である必要があります。
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});
DotNet.invokeMethodAsync
は、操作の結果を表す JS Promise
を返します。 DotNet.invokeMethod
(クライアント側コンポーネント) は操作の結果を返します。
重要
サーバー側コンポーネントでは、同期バージョン (invokeMethod
) 経由よりも非同期関数 (invokeMethodAsync
) を推奨します。
.NET メソッドはパブリックかつ静的であり、[JSInvokable]
属性を持つ必要があります。
次に例を示します。
{<T>}
プレースホルダーは戻り値の型を示します。これは、値を返すメソッドにのみ必要です。{.NET METHOD ID}
プレースホルダーはメソッド識別子です。
@code {
[JSInvokable]
public static Task{<T>} {.NET METHOD ID}()
{
...
}
}
Note
オープン ジェネリック メソッドを呼び出すことは、静的な .NET メソッドではサポートされていませんが、インスタンス メソッドではサポートされています。 詳細については、「.NET ジェネリック クラス メソッドを呼び出す」のセクションを参照してください。
次のコンポーネントでは、ReturnArrayAsync
C# メソッドによって int
配列が返されます。 [JSInvokable]
属性 がメソッドに適用され、JS によってメソッドが呼び出し可能になります。
CallDotnet1.razor
:
@page "/call-dotnet-1"
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 1</PageTitle>
<h1>Call .NET Example 1</h1>
<p>
<button id="btn">Trigger .NET static method</button>
</p>
<p>
See the result in the developer tools console.
</p>
@code {
private IJSObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/Pages/CallDotnet1.razor.js");
await module.InvokeVoidAsync("addHandlers");
}
}
[JSInvokable]
public static Task<int[]> ReturnArrayAsync() =>
Task.FromResult(new int[] { 11, 12, 13 });
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
CallDotnet1.razor.js
:
export function returnArrayAsync() {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
.then(data => {
console.log(data);
});
}
export function addHandlers() {
const btn = document.getElementById("btn");
btn.addEventListener("click", returnArrayAsync);
}
addHandlers
JS 関数は、ボタンに click
イベントを追加します。 returnArrayAsync
JS 関数はハンドラーとして割り当てられます。
returnArrayAsync
JS 関数では、コンポーネントの ReturnArrayAsync
.NET メソッドが呼び出され、その結果はブラウザーの Web 開発者ツール コンソールに記録されます。 BlazorSample
はアプリのアセンブリ名です。
CallDotnet1.razor
:
@page "/call-dotnet-1"
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 1</PageTitle>
<h1>Call .NET Example 1</h1>
<p>
<button id="btn">Trigger .NET static method</button>
</p>
<p>
See the result in the developer tools console.
</p>
@code {
private IJSObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/Pages/CallDotnet1.razor.js");
await module.InvokeVoidAsync("addHandlers");
}
}
[JSInvokable]
public static Task<int[]> ReturnArrayAsync() =>
Task.FromResult(new int[] { 11, 12, 13 });
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
CallDotnet1.razor.js
:
export function returnArrayAsync() {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
.then(data => {
console.log(data);
});
}
export function addHandlers() {
const btn = document.getElementById("btn");
btn.addEventListener("click", returnArrayAsync);
}
addHandlers
JS 関数は、ボタンに click
イベントを追加します。 returnArrayAsync
JS 関数はハンドラーとして割り当てられます。
returnArrayAsync
JS 関数では、コンポーネントの ReturnArrayAsync
.NET メソッドが呼び出され、その結果はブラウザーの Web 開発者ツール コンソールに記録されます。 BlazorSample
はアプリのアセンブリ名です。
CallDotNetExample1.razor
:
@page "/call-dotnet-example-1"
<h1>Call .NET Example 1</h1>
<p>
<button onclick="returnArrayAsync()">
Trigger .NET static method
</button>
</p>
@code {
[JSInvokable]
public static Task<int[]> ReturnArrayAsync()
{
return Task.FromResult(new int[] { 1, 2, 3 });
}
}
CallDotNetExample1.razor
:
@page "/call-dotnet-example-1"
<h1>Call .NET Example 1</h1>
<p>
<button onclick="returnArrayAsync()">
Trigger .NET static method
</button>
</p>
@code {
[JSInvokable]
public static Task<int[]> ReturnArrayAsync()
{
return Task.FromResult(new int[] { 1, 2, 3 });
}
}
CallDotNetExample1.razor
:
@page "/call-dotnet-example-1"
<h1>Call .NET Example 1</h1>
<p>
<button onclick="returnArrayAsync()">
Trigger .NET static method
</button>
</p>
@code {
[JSInvokable]
public static Task<int[]> ReturnArrayAsync()
{
return Task.FromResult(new int[] { 1, 2, 3 });
}
}
CallDotNetExample1.razor
:
@page "/call-dotnet-example-1"
<h1>Call .NET Example 1</h1>
<p>
<button onclick="returnArrayAsync()">
Trigger .NET static method
</button>
</p>
@code {
[JSInvokable]
public static Task<int[]> ReturnArrayAsync()
{
return Task.FromResult(new int[] { 1, 2, 3 });
}
}
<button>
要素の onclick
HTML 属性は、 の @onclick
ディレクティブ属性ではなく、Blazorclick
イベントを処理するための JavaScript の onclick
イベント ハンドラー割り当てです。 returnArrayAsync
JS 関数はハンドラーとして割り当てられます。
次の returnArrayAsync
JS 関数では、コンポーネントの ReturnArrayAsync
.NET メソッドが呼び出され、その結果はブラウザーの Web 開発者ツール コンソールに記録されます。 BlazorSample
はアプリのアセンブリ名です。
<script>
window.returnArrayAsync = () => {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
.then(data => {
console.log(data);
});
};
</script>
Note
JS の場所と実稼働アプリの推奨事項に関する一般的なガイダンスについては、ASP.NET Core Blazor アプリの JavaScript の位置に関する記事を参照してください。
Trigger .NET static method
ボタンが選択されている場合は、ブラウザーの開発者ツール コンソールの出力に配列データが表示されます。 出力の形式はブラウザーによって若干異なります。 次の出力は、Microsoft Edge で使用される形式を示しています。
Array(3) [ 11, 12, 13 ]
データを引数として渡して invokeMethodAsync
関数を呼び出すときに、.NET メソッドにデータを渡します。
.NET にデータを渡す方法を示すには、JS でメソッドが呼び出される ReturnArrayAsync
メソッドに開始位置を渡します。
export function returnArrayAsync() {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync', 14)
.then(data => {
console.log(data);
});
}
<script>
window.returnArrayAsync = () => {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync', 14)
.then(data => {
console.log(data);
});
};
</script>
コンポーネントの呼び出し可能 ReturnArrayAsync
メソッドは開始位置を受け取り、そこから配列を構築します。 コンソールにログするために、配列が返されます。
[JSInvokable]
public static Task<int[]> ReturnArrayAsync(int startPosition) =>
Task.FromResult(Enumerable.Range(startPosition, 3).ToArray());
アプリが再コンパイルされ、ブラウザーが更新されると、ボタンが選択されたときに、ブラウザーのコンソールに次の出力が表示されます。
Array(3) [ 14, 15, 16 ]
JS 呼び出しの .NET メソッド識別子は .NET メソッド名ですが、[JSInvokable]
属性コンストラクターを使用して別の識別子を指定することもできます。 次の例では、DifferentMethodName
は ReturnArrayAsync
メソッドに割り当てられたメソッド識別子です。
[JSInvokable("DifferentMethodName")]
DotNet.invokeMethodAsync
(サーバー側コンポーネントまたはクライアント側コンポーネント) または DotNet.invokeMethod
(クライアント側コンポーネントのみ) の呼び出しでは、DifferentMethodName
を呼び出して ReturnArrayAsync
.NETメソッドを実行します。
DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
DotNet.invokeMethod('BlazorSample', 'DifferentMethodName');
(クライアント側コンポーネントのみ)
Note
このセクションの ReturnArrayAsync
メソッドの例では、明示的な C# async
および await
キーワードを使用せずに Task の結果を返します。 async
および await
を使用したメソッドのコーディングは、await
キーワードを使用して非同期操作の値を返す一般的なメソッドです。
async
および await
キーワードで構成される ReturnArrayAsync
メソッド:
[JSInvokable]
public static async Task<int[]> ReturnArrayAsync() =>
await Task.FromResult(new int[] { 11, 12, 13 });
詳細については、C# ガイドの「Async および Await を使用した非同期プログラミング」を参照してください。
.NET に渡す JavaScript オブジェクトとデータ参照を作成する
DotNet.createJSObjectReference(jsObject)
を呼び出して、.NET に渡すことができるように JS オブジェクト参照を構築します。jsObject
は JS オブジェクト参照の作成に使われる JS Object
です。 次の例では、シリアル化できない window
オブジェクトへの参照を .NET に渡します。これはその参照を ReceiveWindowObject
C# メソッドで IJSObjectReference として受け取ります。
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', 'ReceiveWindowObject',
DotNet.createJSObjectReference(window));
[JSInvokable]
public static void ReceiveWindowObject(IJSObjectReference objRef)
{
...
}
前の例で、{ASSEMBLY NAME}
プレースホルダーはアプリの名前空間です。
Note
前の例では、window
オブジェクトへの参照が JS に保持されないため、JSObjectReference
の破棄は必要ありません。
JSObjectReference
への参照を維持したら、クライアントでの JS メモリのリークを避けるため、それを破棄する必要があります。 次の例では、前のコードをリファクタリングして JSObjectReference
への参照をキャプチャした後、DotNet.disposeJSObjectReference()
を呼び出して参照を破棄します。
var jsObjectReference = DotNet.createJSObjectReference(window);
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', 'ReceiveWindowObject', jsObjectReference);
DotNet.disposeJSObjectReference(jsObjectReference);
前の例で、{ASSEMBLY NAME}
プレースホルダーはアプリの名前空間です。
DotNet.createJSStreamReference(streamReference)
を呼び出して JS ストリーム参照を構築し、.NET に渡すことができるようにします。streamReference
は、ArrayBuffer
、Blob
、または Uint8Array
や Float32Array
などの任意の型指定された配列であり、JS ストリーム参照を作成するために使用されます。
インスタンス .NET メソッドの呼び出し
JavaScript (JS) からインスタンス .NET メソッドを呼び出す場合:
インスタンスを DotNetObjectReference でラップし、その上で Create を呼び出すことにより、JS を参照して .NET インスタンスを渡します。
渡された DotNetObjectReference の
invokeMethodAsync
(推奨) またはinvokeMethod
(クライアント側コンポーネントのみ) を使用して、JS から .NET インスタンス メソッドを呼び出します。 インスタンス .NET メソッドの識別子とすべての引数を渡します。 .NET インスタンスは、JS から他の .NET メソッドを呼び出すときに引数として渡すこともできます。次に例を示します。
dotNetHelper
が DotNetObjectReference です。{.NET METHOD ID}
プレースホルダーは .NET メソッド識別子です。{ARGUMENTS}
プレースホルダーは、メソッドに渡す省略可能なコンマ区切りの引数であり、各引数は JSON シリアル化可能である必要があります。
dotNetHelper.invokeMethodAsync('{.NET METHOD ID}', {ARGUMENTS});
Note
インスタンス メソッドを呼び出すときは、
invokeMethodAsync
とinvokeMethod
はアセンブリ名パラメーターを受け付けません。invokeMethodAsync
は、操作の結果を表す JSPromise
を返します。invokeMethod
(クライアント側コンポーネントのみ) は操作の結果を返します。重要
サーバー側コンポーネントでは、同期バージョン (
invokeMethod
) 経由よりも非同期関数 (invokeMethodAsync
) を推奨します。
この記事の以降のセクションでは、インスタンス .NET メソッドを呼び出すさまざまな方法について説明します。
JavaScript で呼び出し可能な .NET メソッドのトリミングを回避する
このセクションは、Ahead-Of-Time (AOT) コンパイルとランタイム再リンクが有効になっているクライアント側アプリに適用されます。
次のセクションに示すいくつかの例は、クラス インスタンスのアプローチに基づいています。ここで、[JSInvokable]
属性でマークされていて、JavaScript で呼び出し可能な .NET メソッドは、Razor コンポーネントではないクラスのメンバーです。 このような .NET メソッドがコンポーネント内にある Razor 場合、それらは、ランタイムの再リンクやトリミングから保護されます。 .NET メソッドが Razor コンポーネントの外部でトリミングされないようにするには、次の例に示すように、クラスのコンストラクターに DynamicDependency
属性を使用してメソッドを実装します。
using System.Diagnostics.CodeAnalysis;
using Microsoft.JSInterop;
public class ExampleClass {
[DynamicDependency(nameof(ExampleJSInvokableMethod))]
public ExampleClass()
{
}
[JSInvokable]
public string ExampleJSInvokableMethod()
{
...
}
}
詳細については、トリミング用に .NET ライブラリを準備する: DynamicDependency に関するページを参照してください。
DotNetObjectReference
を個々の JavaScript 関数に渡す
このセクションの例は、DotNetObjectReference を個々の JavaScript (JS) 関数に渡す方法を示しています。
次の sayHello1
JS 関数では DotNetObjectReference を受け取り、invokeMethodAsync
を呼び出してコンポーネントの GetHelloMessage
.NET メソッドを呼び出します。
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Note
JS の場所と実稼働アプリの推奨事項に関する一般的なガイダンスについては、ASP.NET Core Blazor アプリの JavaScript の位置に関する記事を参照してください。
前の例で、変数名 dotNetHelper
は任意であり、任意の希望する名前に変更できます。
次の コンポーネントの場合:
- コンポーネントには、
GetHelloMessage
という名前の JS 呼び出し可能な .NET メソッドが含まれます。 Trigger .NET instance method
ボタンを選択すると、JS 関数sayHello1
が DotNetObjectReference と一緒に呼び出されます。sayHello1
:GetHelloMessage
を呼び出し、メッセージ結果を受信します。- メッセージの結果を呼び出し元の
TriggerDotNetInstanceMethod
メソッドに返します。
result
のsayHello1
から返されたメッセージがユーザーに表示されます。- メモリ リークを回避し、ガベージ コレクションを許可するには、DotNetObjectReference によって作成された .NET オブジェクト参照は
Dispose
メソッドで破棄されます。
CallDotnet2.razor
:
@page "/call-dotnet-2"
@implements IDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 2</PageTitle>
<h1>Call .NET Example 2</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string? name;
private string? result;
private DotNetObjectReference<CallDotnet2>? objRef;
protected override void OnInitialized() =>
objRef = DotNetObjectReference.Create(this);
public async Task TriggerDotNetInstanceMethod() =>
result = await JS.InvokeAsync<string>("sayHello1", objRef);
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
public void Dispose() => objRef?.Dispose();
}
CallDotnet2.razor
:
@page "/call-dotnet-2"
@implements IDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 2</PageTitle>
<h1>Call .NET Example 2</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string? name;
private string? result;
private DotNetObjectReference<CallDotnet2>? objRef;
protected override void OnInitialized() =>
objRef = DotNetObjectReference.Create(this);
public async Task TriggerDotNetInstanceMethod() =>
result = await JS.InvokeAsync<string>("sayHello1", objRef);
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
public void Dispose() => objRef?.Dispose();
}
CallDotNetExample2.razor
:
@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call .NET Example 2</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string? name;
private string? result;
private DotNetObjectReference<CallDotNetExample2>? objRef;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task TriggerDotNetInstanceMethod()
{
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
public void Dispose()
{
objRef?.Dispose();
}
}
CallDotNetExample2.razor
:
@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call .NET Example 2</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string? name;
private string? result;
private DotNetObjectReference<CallDotNetExample2>? objRef;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task TriggerDotNetInstanceMethod()
{
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
public void Dispose()
{
objRef?.Dispose();
}
}
CallDotNetExample2.razor
:
@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call .NET Example 2</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string name;
private string result;
private DotNetObjectReference<CallDotNetExample2> objRef;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task TriggerDotNetInstanceMethod()
{
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
public void Dispose()
{
objRef?.Dispose();
}
}
CallDotNetExample2.razor
:
@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call .NET Example 2</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string name;
private string result;
private DotNetObjectReference<CallDotNetExample2> objRef;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task TriggerDotNetInstanceMethod()
{
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
public void Dispose()
{
objRef?.Dispose();
}
}
前の例で、変数名 dotNetHelper
は任意であり、任意の希望する名前に変更できます。
インスタンス メソッドに引数を渡すには、以下のガイダンスを使用します。
.NET メソッドの呼び出しにパラメーターを追加します。 次の例では、名前がメソッドに渡されます。 必要に応じて、追加のパラメーターを一覧に追加します。
<script>
window.sayHello2 = (dotNetHelper, name) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage', name);
};
</script>
前の例で、変数名 dotNetHelper
は任意であり、任意の希望する名前に変更できます。
.NET メソッドにパラメーター リストを指定します。
CallDotnet3.razor
:
@page "/call-dotnet-3"
@implements IDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 3</PageTitle>
<h1>Call .NET Example 3</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string? name;
private string? result;
private DotNetObjectReference<CallDotnet3>? objRef;
protected override void OnInitialized() =>
objRef = DotNetObjectReference.Create(this);
public async Task TriggerDotNetInstanceMethod() =>
result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
[JSInvokable]
public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";
public void Dispose() => objRef?.Dispose();
}
CallDotnet3.razor
:
@page "/call-dotnet-3"
@implements IDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 3</PageTitle>
<h1>Call .NET Example 3</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string? name;
private string? result;
private DotNetObjectReference<CallDotnet3>? objRef;
protected override void OnInitialized() =>
objRef = DotNetObjectReference.Create(this);
public async Task TriggerDotNetInstanceMethod() =>
result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
[JSInvokable]
public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";
public void Dispose() => objRef?.Dispose();
}
CallDotNetExample3.razor
:
@page "/call-dotnet-example-3"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call .NET Example 3</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string? name;
private string? result;
private DotNetObjectReference<CallDotNetExample3>? objRef;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task TriggerDotNetInstanceMethod()
{
result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
}
[JSInvokable]
public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";
public void Dispose()
{
objRef?.Dispose();
}
}
CallDotNetExample3.razor
:
@page "/call-dotnet-example-3"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call .NET Example 3</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string? name;
private string? result;
private DotNetObjectReference<CallDotNetExample3>? objRef;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task TriggerDotNetInstanceMethod()
{
result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
}
[JSInvokable]
public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";
public void Dispose()
{
objRef?.Dispose();
}
}
CallDotNetExample3.razor
:
@page "/call-dotnet-example-3"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call .NET Example 3</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string name;
private string result;
private DotNetObjectReference<CallDotNetExample3> objRef;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task TriggerDotNetInstanceMethod()
{
result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
}
[JSInvokable]
public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";
public void Dispose()
{
objRef?.Dispose();
}
}
CallDotNetExample3.razor
:
@page "/call-dotnet-example-3"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call .NET Example 3</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string name;
private string result;
private DotNetObjectReference<CallDotNetExample3> objRef;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task TriggerDotNetInstanceMethod()
{
result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
}
[JSInvokable]
public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";
public void Dispose()
{
objRef?.Dispose();
}
}
前の例で、変数名 dotNetHelper
は任意であり、任意の希望する名前に変更できます。
複数の JavaScript 関数を持つクラスに DotNetObjectReference
を渡す
このセクションの例は、DotNetObjectReference を複数の関数を持つ JavaScript (JS) クラスに渡す方法を示しています。
DotNetObjectReference を作成して OnAfterRenderAsync
ライフサイクル メソッド から JS クラスに渡して、複数の関数で使用できるようにします。 次の例に示すように、.NET コードで DotNetObjectReference が破棄されることを確認します。
次のコンポーネントでは、[Trigger JS function
] ボタンは、Blazor の @onclick
ディレクティブ属性ではなく、JSonclick
プロパティを設定して JS を呼び出します。
CallDotNetExampleOneHelper.razor
:
@page "/call-dotnet-example-one-helper"
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET Example</PageTitle>
<h1>Pass <code>DotNetObjectReference</code> to a JavaScript class</h1>
<p>
<label>
Message: <input @bind="name" />
</label>
</p>
<p>
<button id="sayHelloBtn">
Trigger JS function <code>sayHello</code>
</button>
</p>
<p>
<button id="welcomeVisitorBtn">
Trigger JS function <code>welcomeVisitor</code>
</button>
</p>
@code {
private IJSObjectReference? module;
private string? name;
private DotNetObjectReference<CallDotNetExampleOneHelper>? dotNetHelper;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/Pages/CallDotNetExampleOneHelper.razor.js");
dotNetHelper = DotNetObjectReference.Create(this);
await module.InvokeVoidAsync("GreetingHelpers.setDotNetHelper",
dotNetHelper);
await module.InvokeVoidAsync("addHandlers");
}
}
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
[JSInvokable]
public string GetWelcomeMessage() => $"Welcome, {name}!";
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
dotNetHelper?.Dispose();
}
}
前の例の場合:
JS
は、挿入された IJSRuntime インスタンスです。 IJSRuntime は Blazor フレームワークによって登録されます。- 変数名
dotNetHelper
は任意であり、任意の希望する名前に変更できます。 - コンポーネントは、ガベージ コレクションを許可し、メモリ リークを防ぐために、DotNetObjectReference を明示的に破棄する必要があります。
- JSDisconnectedException は、 Blazorの SignalR 回路が失われた場合に、モジュールの破棄中にトラップされます。 上記のコードが Blazor WebAssembly アプリで使用されている場合、失われる SignalR 接続がないため、
try
-catch
ブロックを削除し、モジュールを破棄する行 (await module.DisposeAsync();
) のままにすることができます。 詳しくは、「ASP.NET Core Blazor JavaScript の相互運用性 (JS 相互運用)」をご覧ください。
CallDotNetExampleOneHelper.razor.js
:
export class GreetingHelpers {
static dotNetHelper;
static setDotNetHelper(value) {
GreetingHelpers.dotNetHelper = value;
}
static async sayHello() {
const msg =
await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetHelloMessage');
alert(`Message from .NET: "${msg}"`);
}
static async welcomeVisitor() {
const msg =
await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetWelcomeMessage');
alert(`Message from .NET: "${msg}"`);
}
}
export function addHandlers() {
const sayHelloBtn = document.getElementById("sayHelloBtn");
sayHelloBtn.addEventListener("click", GreetingHelpers.sayHello);
const welcomeVisitorBtn = document.getElementById("welcomeVisitorBtn");
welcomeVisitorBtn.addEventListener("click", GreetingHelpers.welcomeVisitor);
}
前の例で、変数名 dotNetHelper
は任意であり、任意の希望する名前に変更できます。
@page "/call-dotnet-example-one-helper"
@implements IDisposable
@inject IJSRuntime JS
<h1>Pass <code>DotNetObjectReference</code> to a JavaScript class</h1>
<p>
<label>
Message: <input @bind="name" />
</label>
</p>
<p>
<button onclick="GreetingHelpers.sayHello()">
Trigger JS function <code>sayHello</code>
</button>
</p>
<p>
<button onclick="GreetingHelpers.welcomeVisitor()">
Trigger JS function <code>welcomeVisitor</code>
</button>
</p>
@code {
private string? name;
private DotNetObjectReference<CallDotNetExampleOneHelper>? dotNetHelper;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
dotNetHelper = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("GreetingHelpers.setDotNetHelper",
dotNetHelper);
}
}
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
[JSInvokable]
public string GetWelcomeMessage() => $"Welcome, {name}!";
public void Dispose()
{
dotNetHelper?.Dispose();
}
}
前の例の場合:
JS
は、挿入された IJSRuntime インスタンスです。 IJSRuntime は Blazor フレームワークによって登録されます。- 変数名
dotNetHelper
は任意であり、任意の希望する名前に変更できます。 - コンポーネントは、ガベージ コレクションを許可し、メモリ リークを防ぐために、DotNetObjectReference を明示的に破棄する必要があります。
<script>
class GreetingHelpers {
static dotNetHelper;
static setDotNetHelper(value) {
GreetingHelpers.dotNetHelper = value;
}
static async sayHello() {
const msg =
await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetHelloMessage');
alert(`Message from .NET: "${msg}"`);
}
static async welcomeVisitor() {
const msg =
await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetWelcomeMessage');
alert(`Message from .NET: "${msg}"`);
}
}
window.GreetingHelpers = GreetingHelpers;
</script>
前の例の場合:
GreetingHelpers
クラスは、window
オブジェクトに追加され、クラスをグローバルに定義します。これにより、Blazor が JS 相互運用のためにクラスを見つけることができます。- 変数名
dotNetHelper
は任意であり、任意の希望する名前に変更できます。
Note
JS の場所に関する一般的なガイダンスと、実稼働アプリの推奨事項については、「ASP.NET Core Blazor アプリでの JavaScript の場所」を参照してください。
NET ジェネリック クラス メソッドを呼び出す
JavaScript (JS) 関数は .NET ジェネリック クラス メソッドを呼び出すことができ、ここで JS 関数はジェネリック クラスの .NET メソッドを呼び出します。
次のジェネリック型クラス (GenericType<TValue>
) で:
- クラスには、1 つのジェネリック
Value
プロパティを持つ 1 つの型パラメーター (TValue
) があります。 - クラスには、
[JSInvokable]
属性でマークされた 2 つの非ジェネリック メソッドがあり、それぞれにnewValue
という名前のジェネリック型パラメーターがあります。Update
はnewValue
からValue
の値を同期的に更新します。UpdateAsync
は、待ち受け時に現在のコンテキストに非同期的に戻す Task.Yield で待ち受け可能なタスクを作成した後newValue
からValue
の値を非同期的に更新します。
- クラスのメソッドはそれぞれ、
TValue
の型とValue
の値をコンソールに書き込みます。 コンソールへの書き込みは、デモンストレーションのみを目的とします。 通常、実稼働アプリでは、アプリのログのため、コンソールへの書き込みを回避します。 詳細については、「ASP.NET Core Blazor のログ」および「.NET Core および ASP.NET Core でのログ記録」を参照してください。
Note
"オープン ジェネリック型とメソッド" では、型プレースホルダーの型は指定されません。 逆に、"クローズド ジェネリック" では、すべての型プレースホルダーの型が指定されます。 このセクションの例は、クローズド ジェネリックを示していますが、オープン ジェネリックでの JS 相互運用 "インスタンス メソッド" の呼び出しは "サポートされています"。 オープン ジェネリックの使用は、この記事で前述した 静的 .NET メソッドの呼び出しではサポートされていません。
詳細については、次の記事をご覧ください。
GenericType.cs
:
using Microsoft.JSInterop;
public class GenericType<TValue>
{
public TValue? Value { get; set; }
[JSInvokable]
public void Update(TValue newValue)
{
Value = newValue;
Console.WriteLine($"Update: GenericType<{typeof(TValue)}>: {Value}");
}
[JSInvokable]
public async Task UpdateAsync(TValue newValue)
{
await Task.Yield();
Value = newValue;
Console.WriteLine($"UpdateAsync: GenericType<{typeof(TValue)}>: {Value}");
}
}
次の invokeMethodsAsync
関数では:
- ジェネリック型クラスの
Update
とUpdateAsync
のメソッドは、文字列と数値を表す引数で呼び出されます。 - クライアント側コンポーネントでは、
invokeMethod
との同期的な .NET メソッドの呼び出しがサポートされています。syncInterop
は、クライアントで JS 相互運用が発生するかどうかを示すブール値を受け取ります。syncInterop
がtrue
の場合、invokeMethod
は安全に呼び出されます。syncInterop
の値がfalse
の場合、JS 相互運用がサーバー側コンポーネントで実行されているため、非同期関数invokeMethodAsync
だけが呼び出されます。 - デモンストレーションの目的で、DotNetObjectReference 関数呼び出し (
invokeMethod
またはinvokeMethodAsync
)、呼び出された .NET メソッド (Update
またはUpdateAsync
) およびおよび引数がコンソールに書き込まれます。 引数は乱数を使用して JS 関数呼び出しと .NET メソッド呼び出しの照合を許可します (.NET 側のコンソールにも書き込まれます)。 通常、実稼働コードは、クライアントまたはサーバー上でコンソールに書き込まれません。 実稼働アプリは通常、アプリのログに依存します。 詳細については、「ASP.NET Core Blazor のログ」および「.NET Core および ASP.NET Core でのログ記録」を参照してください。
<script>
const randomInt = () => Math.floor(Math.random() * 99999);
window.invokeMethodsAsync = async (syncInterop, dotNetHelper1, dotNetHelper2) => {
var n = randomInt();
console.log(`JS: invokeMethodAsync:Update('string ${n}')`);
await dotNetHelper1.invokeMethodAsync('Update', `string ${n}`);
n = randomInt();
console.log(`JS: invokeMethodAsync:UpdateAsync('string ${n}')`);
await dotNetHelper1.invokeMethodAsync('UpdateAsync', `string ${n}`);
if (syncInterop) {
n = randomInt();
console.log(`JS: invokeMethod:Update('string ${n}')`);
dotNetHelper1.invokeMethod('Update', `string ${n}`);
}
n = randomInt();
console.log(`JS: invokeMethodAsync:Update(${n})`);
await dotNetHelper2.invokeMethodAsync('Update', n);
n = randomInt();
console.log(`JS: invokeMethodAsync:UpdateAsync(${n})`);
await dotNetHelper2.invokeMethodAsync('UpdateAsync', n);
if (syncInterop) {
n = randomInt();
console.log(`JS: invokeMethod:Update(${n})`);
dotNetHelper2.invokeMethod('Update', n);
}
};
</script>
Note
JS の場所と実稼働アプリの推奨事項に関する一般的なガイダンスについては、ASP.NET Core Blazor アプリの JavaScript の位置に関する記事を参照してください。
次の GenericsExample
コンポーネントでは、以下のことを行います。
Invoke Interop
ボタンを選択すると、JS 関数invokeMethodsAsync
が呼び出されます。- DotNetObjectReference 型のペアが作成され、
string
およびint
としてGenericType
のインスタンスの JS 関数に渡されます。
GenericsExample.razor
:
@page "/generics-example"
@implements IDisposable
@inject IJSRuntime JS
<p>
<button @onclick="InvokeInterop">Invoke Interop</button>
</p>
<ul>
<li>genericType1: @genericType1?.Value</li>
<li>genericType2: @genericType2?.Value</li>
</ul>
@code {
private GenericType<string> genericType1 = new() { Value = "string 0" };
private GenericType<int> genericType2 = new() { Value = 0 };
private DotNetObjectReference<GenericType<string>>? objRef1;
private DotNetObjectReference<GenericType<int>>? objRef2;
protected override void OnInitialized()
{
objRef1 = DotNetObjectReference.Create(genericType1);
objRef2 = DotNetObjectReference.Create(genericType2);
}
public async Task InvokeInterop()
{
var syncInterop = OperatingSystem.IsBrowser();
await JS.InvokeVoidAsync(
"invokeMethodsAsync", syncInterop, objRef1, objRef2);
}
public void Dispose()
{
objRef1?.Dispose();
objRef2?.Dispose();
}
}
@page "/generics-example"
@implements IDisposable
@inject IJSRuntime JS
<p>
<button @onclick="InvokeInterop">Invoke Interop</button>
</p>
<ul>
<li>genericType1: @genericType1?.Value</li>
<li>genericType2: @genericType2?.Value</li>
</ul>
@code {
private GenericType<string> genericType1 = new() { Value = "string 0" };
private GenericType<int> genericType2 = new() { Value = 0 };
private DotNetObjectReference<GenericType<string>>? objRef1;
private DotNetObjectReference<GenericType<int>>? objRef2;
protected override void OnInitialized()
{
objRef1 = DotNetObjectReference.Create(genericType1);
objRef2 = DotNetObjectReference.Create(genericType2);
}
public async Task InvokeInterop()
{
var syncInterop = OperatingSystem.IsBrowser();
await JS.InvokeVoidAsync(
"invokeMethodsAsync", syncInterop, objRef1, objRef2);
}
public void Dispose()
{
objRef1?.Dispose();
objRef2?.Dispose();
}
}
前の例の JS
は、挿入された IJSRuntime インスタンスです。 IJSRuntime は Blazor フレームワークによって登録されます。
クライアント側コンポーネントで Invoke Interop
ボタンが選択されている場合の、前の例の一般的な出力を次に示します。
JS: invokeMethodAsync:Update('string 37802')
.NET: Update: GenericType<System.String>: string 37802
JS: invokeMethodAsync:UpdateAsync('string 53051')
JS: invokeMethod:Update('string 26784')
.NET: Update: GenericType<System.String>: string 26784
JS: invokeMethodAsync:Update(14107)
.NET: Update: GenericType<System.Int32>: 14107
JS: invokeMethodAsync:UpdateAsync(48995)
JS: invokeMethod:Update(12872)
.NET: Update: GenericType<System.Int32>: 12872
.NET: UpdateAsync: GenericType<System.String>: string 53051
.NET: UpdateAsync: GenericType<System.Int32>: 48995
前の例がサーバー側コンポーネントに実装されている場合、invokeMethod
を使用した同期呼び出しは回避されます。 サーバー側コンポーネントでは、同期バージョン (invokeMethod
) 経由よりも非同期関数 (invokeMethodAsync
) を推奨します。
サーバー側コンポーネントの一般的な出力
JS: invokeMethodAsync:Update('string 34809')
.NET: Update: GenericType<System.String>: string 34809
JS: invokeMethodAsync:UpdateAsync('string 93059')
JS: invokeMethodAsync:Update(41997)
.NET: Update: GenericType<System.Int32>: 41997
JS: invokeMethodAsync:UpdateAsync(24652)
.NET: UpdateAsync: GenericType<System.String>: string 93059
.NET: UpdateAsync: GenericType<System.Int32>: 24652
上記の出力例は、スレッドのスケジューリングやメソッドの実行速度など、いくつかの要因に応じて、非同期メソッドが "任意の順序で" 実行され、完了する例を示しています。 非同期メソッド呼び出しの完了順序は、確実には予測できません。
クラス インスタンスの例
次の sayHello1
JS 関数:
- 渡された DotNetObjectReference で
GetHelloMessage
.NET メソッドを呼び出します。 GetHelloMessage
からsayHello1
の呼び出し元にメッセージを返します。
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Note
JS の場所と実稼働アプリの推奨事項に関する一般的なガイダンスについては、ASP.NET Core Blazor アプリの JavaScript の位置に関する記事を参照してください。
前の例で、変数名 dotNetHelper
は任意であり、任意の希望する名前に変更できます。
次の HelloHelper
クラスには、GetHelloMessage
という名前の JS 呼び出し可能な .NET メソッドが含まれます。 HelloHelper
が作成されると、Name
プロパティの名前が GetHelloMessage
からのメッセージを返すために使用されます。
HelloHelper.cs
:
using Microsoft.JSInterop;
namespace BlazorSample;
public class HelloHelper(string? name)
{
public string? Name { get; set; } = name ?? "No Name";
[JSInvokable]
public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;
namespace BlazorSample;
public class HelloHelper(string? name)
{
public string? Name { get; set; } = name ?? "No Name";
[JSInvokable]
public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;
public class HelloHelper
{
public HelloHelper(string? name)
{
Name = name ?? "No Name";
}
public string? Name { get; set; }
[JSInvokable]
public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;
public class HelloHelper
{
public HelloHelper(string? name)
{
Name = name ?? "No Name";
}
public string? Name { get; set; }
[JSInvokable]
public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;
public class HelloHelper
{
public HelloHelper(string name)
{
Name = name;
}
public string Name { get; set; }
[JSInvokable]
public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;
public class HelloHelper
{
public HelloHelper(string name)
{
Name = name;
}
public string Name { get; set; }
[JSInvokable]
public string GetHelloMessage() => $"Hello, {Name}!";
}
次の JsInteropClasses3
クラスの CallHelloHelperGetHelloMessage
メソッドでは、HelloHelper
の新しいインスタンスで JS 関数 sayHello1
を呼び出します。
JsInteropClasses3.cs
:
using Microsoft.JSInterop;
namespace BlazorSample;
public class JsInteropClasses3(IJSRuntime js)
{
private readonly IJSRuntime js = js;
public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
return await js.InvokeAsync<string>("sayHello1", objRef);
}
}
using Microsoft.JSInterop;
namespace BlazorSample;
public class JsInteropClasses3(IJSRuntime js)
{
private readonly IJSRuntime js = js;
public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
return await js.InvokeAsync<string>("sayHello1", objRef);
}
}
using Microsoft.JSInterop;
public class JsInteropClasses3
{
private readonly IJSRuntime js;
public JsInteropClasses3(IJSRuntime js)
{
this.js = js;
}
public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
return await js.InvokeAsync<string>("sayHello1", objRef);
}
}
using Microsoft.JSInterop;
public class JsInteropClasses3
{
private readonly IJSRuntime js;
public JsInteropClasses3(IJSRuntime js)
{
this.js = js;
}
public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
return await js.InvokeAsync<string>("sayHello1", objRef);
}
}
using System.Threading.Tasks;
using Microsoft.JSInterop;
public class JsInteropClasses3
{
private readonly IJSRuntime js;
public JsInteropClasses3(IJSRuntime js)
{
this.js = js;
}
public async ValueTask<string> CallHelloHelperGetHelloMessage(string name)
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
return await js.InvokeAsync<string>("sayHello1", objRef);
}
}
using System.Threading.Tasks;
using Microsoft.JSInterop;
public class JsInteropClasses3
{
private readonly IJSRuntime js;
public JsInteropClasses3(IJSRuntime js)
{
this.js = js;
}
public async ValueTask<string> CallHelloHelperGetHelloMessage(string name)
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
return await js.InvokeAsync<string>("sayHello1", objRef);
}
}
メモリ リークを回避し、ガベージ コレクションを許可するため、DotNetObjectReference によって作成された .NET オブジェクト参照は、そのオブジェクト参照が using var
構文でスコープ外になったときに破棄されます。
次のコンポーネントで [Trigger .NET instance method
] ボタンを選択すると、name
の値で JsInteropClasses3.CallHelloHelperGetHelloMessage
が呼び出されます。
CallDotnet4.razor
:
@page "/call-dotnet-4"
@inject IJSRuntime JS
<PageTitle>Call .NET 4</PageTitle>
<h1>Call .NET Example 4</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string? name;
private string? result;
private JsInteropClasses3? jsInteropClasses;
protected override void OnInitialized() =>
jsInteropClasses = new JsInteropClasses3(JS);
private async Task TriggerDotNetInstanceMethod()
{
if (jsInteropClasses is not null)
{
result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
}
}
}
CallDotnet4.razor
:
@page "/call-dotnet-4"
@inject IJSRuntime JS
<PageTitle>Call .NET 4</PageTitle>
<h1>Call .NET Example 4</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string? name;
private string? result;
private JsInteropClasses3? jsInteropClasses;
protected override void OnInitialized() =>
jsInteropClasses = new JsInteropClasses3(JS);
private async Task TriggerDotNetInstanceMethod()
{
if (jsInteropClasses is not null)
{
result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
}
}
}
CallDotNetExample4.razor
:
@page "/call-dotnet-example-4"
@inject IJSRuntime JS
<h1>Call .NET Example 4</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string? name;
private string? result;
private JsInteropClasses3? jsInteropClasses;
protected override void OnInitialized()
{
jsInteropClasses = new JsInteropClasses3(JS);
}
private async Task TriggerDotNetInstanceMethod()
{
if (jsInteropClasses is not null)
{
result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
}
}
}
CallDotNetExample4.razor
:
@page "/call-dotnet-example-4"
@inject IJSRuntime JS
<h1>Call .NET Example 4</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string? name;
private string? result;
private JsInteropClasses3? jsInteropClasses;
protected override void OnInitialized()
{
jsInteropClasses = new JsInteropClasses3(JS);
}
private async Task TriggerDotNetInstanceMethod()
{
if (jsInteropClasses is not null)
{
result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
}
}
}
CallDotNetExample4.razor
:
@page "/call-dotnet-example-4"
@inject IJSRuntime JS
<h1>Call .NET Example 4</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string name;
private string result;
private JsInteropClasses3 jsInteropClasses;
protected override void OnInitialized()
{
jsInteropClasses = new JsInteropClasses3(JS);
}
private async Task TriggerDotNetInstanceMethod()
{
result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
}
}
CallDotNetExample4.razor
:
@page "/call-dotnet-example-4"
@inject IJSRuntime JS
<h1>Call .NET Example 4</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string name;
private string result;
private JsInteropClasses3 jsInteropClasses;
protected override void OnInitialized()
{
jsInteropClasses = new JsInteropClasses3(JS);
}
private async Task TriggerDotNetInstanceMethod()
{
result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
}
}
次の図は、Name
フィールドに Amy Pond
という名前でレンダリングされたコンポーネントを示しています。 ボタンを選択すると、UI に Hello, Amy Pond!
が表示されます。
JsInteropClasses3
クラスに示されている上記のパターンは、完全にコンポーネントに実装することもできます。
CallDotnet5.razor
:
@page "/call-dotnet-5"
@inject IJSRuntime JS
<PageTitle>Call .NET 5</PageTitle>
<h1>Call .NET Example 5</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string? name;
private string? result;
public async Task TriggerDotNetInstanceMethod()
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
}
CallDotnet5.razor
:
@page "/call-dotnet-5"
@inject IJSRuntime JS
<PageTitle>Call .NET 5</PageTitle>
<h1>Call .NET Example 5</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string? name;
private string? result;
public async Task TriggerDotNetInstanceMethod()
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
}
CallDotNetExample5.razor
:
@page "/call-dotnet-example-5"
@inject IJSRuntime JS
<h1>Call .NET Example 5</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string? name;
private string? result;
public async Task TriggerDotNetInstanceMethod()
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
}
CallDotNetExample5.razor
:
@page "/call-dotnet-example-5"
@inject IJSRuntime JS
<h1>Call .NET Example 5</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string? name;
private string? result;
public async Task TriggerDotNetInstanceMethod()
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
}
CallDotNetExample5.razor
:
@page "/call-dotnet-example-5"
@inject IJSRuntime JS
<h1>Call .NET Example 5</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string name;
private string result;
public async Task TriggerDotNetInstanceMethod()
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
}
CallDotNetExample5.razor
:
@page "/call-dotnet-example-5"
@inject IJSRuntime JS
<h1>Call .NET Example 5</h1>
<p>
<label>
Name: <input @bind="name" />
</label>
</p>
<p>
<button @onclick="TriggerDotNetInstanceMethod">
Trigger .NET instance method
</button>
</p>
<p>
@result
</p>
@code {
private string name;
private string result;
public async Task TriggerDotNetInstanceMethod()
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
}
メモリ リークを回避し、ガベージ コレクションを許可するため、DotNetObjectReference によって作成された .NET オブジェクト参照は、そのオブジェクト参照が using var
構文でスコープ外になったときに破棄されます。
name
フィールドに名前 Amy Pond
が指定されている場合、コンポーネントによって表示される出力は Hello, Amy Pond!
です。
前の コンポーネントでは、.NET オブジェクト参照は破棄されます。 クラスまたはコンポーネントが DotNetObjectReference を破棄しない場合は、渡された DotNetObjectReference で dispose
を呼び出して、クライアントから破棄します。
window.{JS FUNCTION NAME} = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('{.NET METHOD ID}');
dotNetHelper.dispose();
}
前の例の場合:
{JS FUNCTION NAME}
プレースホルダーは JS 関数の名前です。- 変数名
dotNetHelper
は任意であり、任意の希望する名前に変更できます。 {.NET METHOD ID}
プレースホルダーは .NET メソッド識別子です。
コンポーネント インスタンス .NET メソッド ヘルパー クラス
ヘルパー クラスでは、.NET インスタンス メソッドを Action として呼び出すことができます。 ヘルパー クラスは、静的な .NET メソッドを使用できないシナリオで役立ちます。
- 同じ種類の複数のコンポーネントが同じページにレンダリングされる場合。
- サーバー側アプリで、複数のユーザーが同じコンポーネントを同時に使用する場合。
次に例を示します。
- コンポーネントには、いくつかの
ListItem1
コンポーネントが含まれています。 - 各
ListItem1
コンポーネントは、メッセージとボタンで構成されます。 ListItem1
コンポーネント ボタンが選択されると、そのListItem1
のUpdateMessage
メソッドによってリスト項目のテキストが変更され、ボタンが非表示になります。
次の MessageUpdateInvokeHelper
クラスでは、クラスがインスタンス化されるときに指定された Action を呼び出すために、JS 呼び出し可能な .NET メソッド UpdateMessageCaller
が保持されます。
MessageUpdateInvokeHelper.cs
:
using Microsoft.JSInterop;
namespace BlazorSample;
public class MessageUpdateInvokeHelper(Action action)
{
private readonly Action action = action;
[JSInvokable]
public void UpdateMessageCaller() => action.Invoke();
}
using Microsoft.JSInterop;
namespace BlazorSample;
public class MessageUpdateInvokeHelper(Action action)
{
private readonly Action action = action;
[JSInvokable]
public void UpdateMessageCaller() => action.Invoke();
}
using Microsoft.JSInterop;
public class MessageUpdateInvokeHelper
{
private Action action;
public MessageUpdateInvokeHelper(Action action)
{
this.action = action;
}
[JSInvokable]
public void UpdateMessageCaller()
{
action.Invoke();
}
}
using Microsoft.JSInterop;
public class MessageUpdateInvokeHelper
{
private Action action;
public MessageUpdateInvokeHelper(Action action)
{
this.action = action;
}
[JSInvokable]
public void UpdateMessageCaller()
{
action.Invoke();
}
}
using System;
using Microsoft.JSInterop;
public class MessageUpdateInvokeHelper
{
private Action action;
public MessageUpdateInvokeHelper(Action action)
{
this.action = action;
}
[JSInvokable]
public void UpdateMessageCaller()
{
action.Invoke();
}
}
using System;
using Microsoft.JSInterop;
public class MessageUpdateInvokeHelper
{
private Action action;
public MessageUpdateInvokeHelper(Action action)
{
this.action = action;
}
[JSInvokable]
public void UpdateMessageCaller()
{
action.Invoke();
}
}
次の updateMessageCaller
JS 関数は、UpdateMessageCaller
.NET メソッドを呼び出します。
<script>
window.updateMessageCaller = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('UpdateMessageCaller');
dotNetHelper.dispose();
}
</script>
Note
JS の場所と実稼働アプリの推奨事項に関する一般的なガイダンスについては、ASP.NET Core Blazor アプリの JavaScript の位置に関する記事を参照してください。
前の例で、変数名 dotNetHelper
は任意であり、任意の希望する名前に変更できます。
次の ListItem1
コンポーネントは、親コンポーネントで何度でも使用できる共有コンポーネントであり、HTML リスト (<ul>...</ul>
または <ol>...</ol>
) のリスト項目 (<li>...</li>
) を作成します。 各 ListItem1
コンポーネント インスタンスは、Action がその UpdateMessage
メソッドに設定された MessageUpdateInvokeHelper
のインスタンスを確立します。
ListItem1
コンポーネントの InteropCall
ボタンが選択された場合、MessageUpdateInvokeHelper
インスタンス用に作成された DotNetObjectReference で updateMessageCaller
が呼び出されます。 これにより、フレームワークはその ListItem1
の MessageUpdateInvokeHelper
インスタンスで UpdateMessageCaller
を呼び出すことができます。 渡された DotNetObjectReference は JS (dotNetHelper.dispose()
) で破棄されます。
ListItem1.razor
:
@inject IJSRuntime JS
<li>
@message
<button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>
@code {
private string message = "Select one of these list item buttons.";
private string display = "inline-block";
private MessageUpdateInvokeHelper? messageUpdateInvokeHelper;
protected override void OnInitialized()
{
messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
}
protected async Task InteropCall()
{
if (messageUpdateInvokeHelper is not null)
{
await JS.InvokeVoidAsync("updateMessageCaller",
DotNetObjectReference.Create(messageUpdateInvokeHelper));
}
}
private void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
}
@inject IJSRuntime JS
<li>
@message
<button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>
@code {
private string message = "Select one of these list item buttons.";
private string display = "inline-block";
private MessageUpdateInvokeHelper? messageUpdateInvokeHelper;
protected override void OnInitialized()
{
messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
}
protected async Task InteropCall()
{
if (messageUpdateInvokeHelper is not null)
{
await JS.InvokeVoidAsync("updateMessageCaller",
DotNetObjectReference.Create(messageUpdateInvokeHelper));
}
}
private void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
}
@inject IJSRuntime JS
<li>
@message
<button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>
@code {
private string message = "Select one of these list item buttons.";
private string display = "inline-block";
private MessageUpdateInvokeHelper? messageUpdateInvokeHelper;
protected override void OnInitialized()
{
messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
}
protected async Task InteropCall()
{
if (messageUpdateInvokeHelper is not null)
{
await JS.InvokeVoidAsync("updateMessageCaller",
DotNetObjectReference.Create(messageUpdateInvokeHelper));
}
}
private void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
}
@inject IJSRuntime JS
<li>
@message
<button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>
@code {
private string message = "Select one of these list item buttons.";
private string display = "inline-block";
private MessageUpdateInvokeHelper? messageUpdateInvokeHelper;
protected override void OnInitialized()
{
messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
}
protected async Task InteropCall()
{
if (messageUpdateInvokeHelper is not null)
{
await JS.InvokeVoidAsync("updateMessageCaller",
DotNetObjectReference.Create(messageUpdateInvokeHelper));
}
}
private void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
}
@inject IJSRuntime JS
<li>
@message
<button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>
@code {
private string message = "Select one of these list item buttons.";
private string display = "inline-block";
private MessageUpdateInvokeHelper messageUpdateInvokeHelper;
protected override void OnInitialized()
{
messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
}
protected async Task InteropCall()
{
await JS.InvokeVoidAsync("updateMessageCaller",
DotNetObjectReference.Create(messageUpdateInvokeHelper));
}
private void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
}
@inject IJSRuntime JS
<li>
@message
<button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>
@code {
private string message = "Select one of these list item buttons.";
private string display = "inline-block";
private MessageUpdateInvokeHelper messageUpdateInvokeHelper;
protected override void OnInitialized()
{
messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
}
protected async Task InteropCall()
{
await JS.InvokeVoidAsync("updateMessageCaller",
DotNetObjectReference.Create(messageUpdateInvokeHelper));
}
private void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
}
message
が UpdateMessage
で設定されている場合、UI を更新するために StateHasChanged
が呼び出されます。 StateHasChanged
が呼び出されていない場合、Blazor には Action が呼び出されたときに UI を更新する必要があることを知る方法はありません。
次の親コンポーネントには、それぞれ ListItem1
コンポーネントのインスタンスである 4 つのリスト項目が含まれています。
CallDotnet6.razor
:
@page "/call-dotnet-6"
<PageTitle>Call .NET 6</PageTitle>
<h1>Call .NET Example 6</h1>
<ul>
<ListItem1 />
<ListItem1 />
<ListItem1 />
<ListItem1 />
</ul>
CallDotnet6.razor
:
@page "/call-dotnet-6"
<PageTitle>Call .NET 6</PageTitle>
<h1>Call .NET Example 6</h1>
<ul>
<ListItem1 />
<ListItem1 />
<ListItem1 />
<ListItem1 />
</ul>
CallDotNetExample6.razor
:
@page "/call-dotnet-example-6"
<h1>Call .NET Example 6</h1>
<ul>
<ListItem1 />
<ListItem1 />
<ListItem1 />
<ListItem1 />
</ul>
CallDotNetExample6.razor
:
@page "/call-dotnet-example-6"
<h1>Call .NET Example 6</h1>
<ul>
<ListItem1 />
<ListItem1 />
<ListItem1 />
<ListItem1 />
</ul>
CallDotNetExample6.razor
:
@page "/call-dotnet-example-6"
<h1>Call .NET Example 6</h1>
<ul>
<ListItem1 />
<ListItem1 />
<ListItem1 />
<ListItem1 />
</ul>
CallDotNetExample6.razor
:
@page "/call-dotnet-example-6"
<h1>Call .NET Example 6</h1>
<ul>
<ListItem1 />
<ListItem1 />
<ListItem1 />
<ListItem1 />
</ul>
次の図は、2 番目の InteropCall
ボタンが選択された後にレンダリングされた親コンポーネントを示しています。
- 2 番目の
ListItem1
コンポーネントによってUpdateMessage Called!
メッセージが表示されました。 - ボタンの CSS
display
プロパティがnone
に設定されているため、2 番目のListItem1
コンポーネントのInteropCall
ボタンは表示されません。
要素プロパティに割り当てられた DotNetObjectReference
から呼び出されるコンポーネント インスタンスの .NET メソッド
HTML 要素のプロパティに対する DotNetObjectReference の割り当てにより、コンポーネント インスタンスの .NET メソッドを呼び出すことができます。
- 要素の参照がキャプチャされます (ElementReference)。
- コンポーネントの
OnAfterRender{Async}
メソッドでは、要素の参照とコンポーネント インスタンスを DotNetObjectReference として、JavaScript (JS) 関数が呼び出されます。 この JS 関数は、DotNetObjectReference をプロパティ内の要素にアタッチします。 - JS で要素イベントが呼び出されると (たとえば
onclick
)、その要素のアタッチされた DotNetObjectReference が .NET メソッドの呼び出しに使用されます。
Component インスタンス .NET メソッド ヘルパー クラスセクションで説明されている方法と同様に、このアプローチは、静的な .NET メソッドを使用できないシナリオで役立ちます。
- 同じ種類の複数のコンポーネントが同じページにレンダリングされる場合。
- サーバー側アプリで、複数のユーザーが同じコンポーネントを同時に使用する場合。
- .NET メソッドが、Blazor イベント (たとえば
@onclick
) からではなく、JS イベント (たとえばonclick
) から呼び出される場合。
次に例を示します。
- コンポーネントには、共有コンポーネントであるいくつかの
ListItem2
コンポーネントが含まれています。 - 各
ListItem2
コンポーネントは、リスト項目メッセージ<span>
と、display
CSS プロパティが表示用にinline-block
に設定された 2 つ目の<span>
で構成されています。 ListItem2
コンポーネントのリスト項目が選択されると、そのListItem2
のUpdateMessage
メソッドによって最初の<span>
のリスト項目テキストが変更され、そのdisplay
プロパティをnone
に設定することで 2 つ目の<span>
が非表示になります。
次の assignDotNetHelper
JS 関数は、dotNetHelper
という名前のプロパティの要素に DotNetObjectReference を割り当てます。 次の interopCall
JS 関数では、渡される要素に DotNetObjectReference を使用して、UpdateMessage
という名前の .NET メソッドを呼び出します。
ListItem2.razor.js
:
export function assignDotNetHelper(element, dotNetHelper) {
element.dotNetHelper = dotNetHelper;
}
export async function interopCall(element) {
await element.dotNetHelper.invokeMethodAsync('UpdateMessage');
}
ListItem2.razor.js
:
export function assignDotNetHelper(element, dotNetHelper) {
element.dotNetHelper = dotNetHelper;
}
export async function interopCall(element) {
await element.dotNetHelper.invokeMethodAsync('UpdateMessage');
}
<script>
window.assignDotNetHelper = (element, dotNetHelper) => {
element.dotNetHelper = dotNetHelper;
}
window.interopCall = async (element) => {
await element.dotNetHelper.invokeMethodAsync('UpdateMessage');
}
</script>
Note
JS の場所と実稼働アプリの推奨事項に関する一般的なガイダンスについては、ASP.NET Core Blazor アプリの JavaScript の位置に関する記事を参照してください。
前の例で、変数名 dotNetHelper
は任意であり、任意の希望する名前に変更できます。
次の ListItem2
コンポーネントは、親コンポーネントで何度でも使用できる共有コンポーネントであり、HTML リスト (<ul>...</ul>
または <ol>...</ol>
) のリスト項目 (<li>...</li>
) を作成します。
各 ListItem2
コンポーネント インスタンスでは、要素の参照 (リスト項目の最初の <span>
要素) とコンポーネント インスタンスを DotNetObjectReference として、OnAfterRenderAsync
で assignDotNetHelper
JS 関数を呼び出します。
ListItem2
コンポーネントのメッセージ <span>
が選択されると、<span>
要素をパラメーター (this
) として渡して interopCall
が呼び出されます。これにより、UpdateMessage
.NET メソッドが呼び出されます。 UpdateMessage
では、message
が設定され、2 つ目の <span>
の display
プロパティが更新されると、StateHasChanged
が呼び出されて UI が更新されます。 StateHasChanged
が呼び出されないと、Blazor にはメソッドが呼び出されたときに UI を更新する必要があることを知る方法がありません。
DotNetObjectReference は、コンポーネントが破棄されるときに破棄されます。
ListItem2.razor
:
@inject IJSRuntime JS
@implements IAsyncDisposable
<li>
<span style="font-weight:bold;color:@color" @ref="elementRef"
@onclick="CallJSToInvokeDotnet">
@message
</span>
<span style="display:@display">
Not Updated Yet!
</span>
</li>
@code {
private IJSObjectReference? module;
private DotNetObjectReference<ListItem2>? objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
private string color = "initial";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/ListItem2.razor.js");
objRef = DotNetObjectReference.Create(this);
await module.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
public async Task CallJSToInvokeDotnet()
{
if (module is not null)
{
await module.InvokeVoidAsync("interopCall", elementRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
color = "MediumSeaGreen";
StateHasChanged();
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
objRef?.Dispose();
}
}
@inject IJSRuntime JS
@implements IAsyncDisposable
<li>
<span style="font-weight:bold;color:@color" @ref="elementRef"
@onclick="CallJSToInvokeDotnet">
@message
</span>
<span style="display:@display">
Not Updated Yet!
</span>
</li>
@code {
private IJSObjectReference? module;
private DotNetObjectReference<ListItem2>? objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
private string color = "initial";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/ListItem2.razor.js");
objRef = DotNetObjectReference.Create(this);
await module.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
public async Task CallJSToInvokeDotnet()
{
if (module is not null)
{
await module.InvokeVoidAsync("interopCall", elementRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
color = "MediumSeaGreen";
StateHasChanged();
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
objRef?.Dispose();
}
}
@inject IJSRuntime JS
<li>
<span @ref="elementRef" onclick="interopCall(this)">@message</span>
<span style="display:@display">Not Updated Yet!</span>
</li>
@code {
private DotNetObjectReference<ListItem2>? objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
objRef = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS
<li>
<span @ref="elementRef" onclick="interopCall(this)">@message</span>
<span style="display:@display">Not Updated Yet!</span>
</li>
@code {
private DotNetObjectReference<ListItem2>? objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
objRef = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS
<li>
<span @ref="elementRef" onclick="interopCall(this)">@message</span>
<span style="display:@display">Not Updated Yet!</span>
</li>
@code {
private DotNetObjectReference<ListItem2> objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
objRef = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS
<li>
<span @ref="elementRef" onclick="interopCall(this)">@message</span>
<span style="display:@display">Not Updated Yet!</span>
</li>
@code {
private DotNetObjectReference<ListItem2> objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
objRef = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
public void Dispose() => objRef?.Dispose();
}
次の親コンポーネントには、それぞれ ListItem2
コンポーネントのインスタンスである 4 つのリスト項目が含まれています。
CallDotnet7.razor
:
@page "/call-dotnet-7"
<PageTitle>Call .NET 7</PageTitle>
<h1>Call .NET Example 7</h1>
<ul>
<ListItem2 />
<ListItem2 />
<ListItem2 />
<ListItem2 />
</ul>
CallDotnet7.razor
:
@page "/call-dotnet-7"
<PageTitle>Call .NET 7</PageTitle>
<h1>Call .NET Example 7</h1>
<ul>
<ListItem2 />
<ListItem2 />
<ListItem2 />
<ListItem2 />
</ul>
CallDotNetExample7.razor
:
@page "/call-dotnet-example-7"
<h1>Call .NET Example 7</h1>
<ul>
<ListItem2 />
<ListItem2 />
<ListItem2 />
<ListItem2 />
</ul>
CallDotNetExample7.razor
:
@page "/call-dotnet-example-7"
<h1>Call .NET Example 7</h1>
<ul>
<ListItem2 />
<ListItem2 />
<ListItem2 />
<ListItem2 />
</ul>
CallDotNetExample7.razor
:
@page "/call-dotnet-example-7"
<h1>Call .NET Example 7</h1>
<ul>
<ListItem2 />
<ListItem2 />
<ListItem2 />
<ListItem2 />
</ul>
CallDotNetExample7.razor
:
@page "/call-dotnet-example-7"
<h1>Call .NET Example 7</h1>
<ul>
<ListItem2 />
<ListItem2 />
<ListItem2 />
<ListItem2 />
</ul>
クライアント側コンポーネントでの同期的 JS 相互運用
このセクションはクライアント側コンポーネントにのみ適用されます。
JS 相互運用呼び出しは、呼び出されたコードが同期であるか非同期であるかに関係なく、非同期となります。 呼び出しが非同期であるのは、サーバー側とクライアント側のレンダリング モードでコンポーネントの互換性を確保するためです。 サーバーでは、すべての JS 相互運用呼び出しはネットワーク接続を介して送信されるため、非同期である必要があります。
コンポーネントが WebAssembly でのみ実行されることが確実にわかっている場合は、同期 JS 相互運用呼び出しを行うように選択できます。 これにより、非同期呼び出しを行う場合よりもオーバーヘッドがわずかに減少し、レンダリング サイクルが少なくなる可能性があります。これは、結果を待機する間の中間状態が存在しないためです。
クライアント側コンポーネントで JavaScript から .NET への同期呼び出しを行うには、DotNet.invokeMethodAsync
ではなく DotNet.invokeMethod
を使用します。
同期呼び出しは、次の場合に機能します。
JavaScript の場所
JavaScript の場所に関する記事で説明されている方法のいずれかを使用して、JavaScript (JS) コードを読み込みます。
JS モジュールを使用して JS を読み込む方法については、この記事の「JavaScript モジュールでの JavaScript の分離」セクションを参照してください。
警告
<script>
タグは動的に更新できないため、コンポーネントが静的サーバー側レンダリング (静的 SSR) を採用することが保証されている場合にのみ、コンポーネント ファイル (.razor
) に <script>
タグを配置します。
警告
<script>
タグは動的に更新できないため、<script>
タグをコンポーネント ファイル (.razor
) 内に配置しないでください。
JavaScript モジュールでの JavaScript の分離
Blazor では、標準の JavaScript モジュールに JavaScript (JS) を分離できます (ECMAScript の仕様)。 JavaScript モジュールの読み込みは、他の種類の Web アプリの場合と同じように Blazor で機能し、アプリでモジュールを定義する方法を自由にカスタマイズできます。 JavaScript モジュールの使用方法に関するガイドについては、MDN Web Docs の「JavaScript モジュール」を参照してください。
JS を分離すると、次のようなベネフィットがあります。
- インポートされる JS によって、グローバル名前空間が汚染されなくなります。
- ライブラリおよびコンポーネントのコンシューマーは、関連する JS をインポートする必要がありません。
詳しくは、「ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出す」をご覧ください。
import()
演算子を使用した動的インポートは、ASP.NET Core と Blazorでサポートされています。
if ({CONDITION}) import("/additionalModule.js");
前述の例では、プレースホルダー {CONDITION}
は、モジュールを読み込む必要があるかどうかを判断するための条件付きチェックを表しています。
ブラウザーの互換性については、「Can I use: JavaScript modules: dynamic import (使用できるかどうか: JavaScript モジュール: dynamic import)」を参照してください。
循環オブジェクト参照の回避
循環参照を含むオブジェクトは、次のいずれに対しても、クライアントでシリアル化することはできません。
- .NET メソッドの呼び出し。
- 戻り値の型に循環参照がある場合の、C# からの JavaScript メソッドの呼び出し。
バイト配列のサポート
Blazor では、Base64 へのバイト配列のエンコードおよびデコードを回避する、最適化されたバイト配列 JavaScript (JS) 相互運用がサポートされています。 次の例では、JS 相互運用を使用してバイト配列を .NET に渡します。
sendByteArray
JS 関数を指定します。 この関数は、コンポーネント内のボタンによって静的に呼び出され (これには、invokeMethodAsync
の呼び出しのアセンブリ名パラメーターが含まれます)、値を返しません。
CallDotnet8.razor.js
:
export function sendByteArray() {
const data = new Uint8Array([0x45, 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69,
0x6e, 0x67, 0x27, 0x73, 0x20, 0x73, 0x68, 0x69, 0x6e, 0x79, 0x2c,
0x20, 0x43, 0x61, 0x70, 0x74, 0x61, 0x69, 0x6e, 0x2e, 0x20, 0x4e,
0x6f, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x72, 0x65, 0x74, 0x2e]);
DotNet.invokeMethodAsync('BlazorSample', 'ReceiveByteArray', data)
.then(str => {
alert(str);
});
}
export function addHandlers() {
const btn = document.getElementById("btn");
btn.addEventListener("click", sendByteArray);
}
<script>
window.sendByteArray = () => {
const data = new Uint8Array([0x45,0x76,0x65,0x72,0x79,0x74,0x68,0x69,
0x6e,0x67,0x27,0x73,0x20,0x73,0x68,0x69,0x6e,0x79,0x2c,
0x20,0x43,0x61,0x70,0x74,0x61,0x69,0x6e,0x2e,0x20,0x4e,
0x6f,0x74,0x20,0x74,0x6f,0x20,0x66,0x72,0x65,0x74,0x2e]);
DotNet.invokeMethodAsync('BlazorSample', 'ReceiveByteArray', data)
.then(str => {
alert(str);
});
};
</script>
Note
JS の場所と実稼働アプリの推奨事項に関する一般的なガイダンスについては、ASP.NET Core Blazor アプリの JavaScript の位置に関する記事を参照してください。
CallDotnet8.razor
:
@page "/call-dotnet-8"
@using System.Text
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 8</PageTitle>
<h1>Call .NET Example 8</h1>
<p>
<button id="btn">Send Bytes</button>
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>
@code {
private IJSObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/Pages/CallDotnet8.razor.js");
await module.InvokeVoidAsync("addHandlers");
}
}
[JSInvokable]
public static Task<string> ReceiveByteArray(byte[] receivedBytes) =>
Task.FromResult(Encoding.UTF8.GetString(receivedBytes, 0,
receivedBytes.Length));
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
CallDotnet8.razor
:
@page "/call-dotnet-8"
@using System.Text
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 8</PageTitle>
<h1>Call .NET Example 8</h1>
<p>
<button id="btn">Send Bytes</button>
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>
@code {
private IJSObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/Pages/CallDotnet8.razor.js");
await module.InvokeVoidAsync("addHandlers");
}
}
[JSInvokable]
public static Task<string> ReceiveByteArray(byte[] receivedBytes) =>
Task.FromResult(Encoding.UTF8.GetString(receivedBytes, 0,
receivedBytes.Length));
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
CallDotNetExample8.razor
:
@page "/call-dotnet-example-8"
@using System.Text
<PageTitle>Call .NET 8</PageTitle>
<h1>Call .NET Example 8</h1>
<p>
<button onclick="sendByteArray()">Send Bytes</button>
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>
@code {
[JSInvokable]
public static Task<string> ReceiveByteArray(byte[] receivedBytes)
{
return Task.FromResult(
Encoding.UTF8.GetString(receivedBytes, 0, receivedBytes.Length));
}
}
CallDotNetExample8.razor
:
@page "/call-dotnet-example-8"
@using System.Text
<h1>Call .NET Example 8</h1>
<p>
<button onclick="sendByteArray()">Send Bytes</button>
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>
@code {
[JSInvokable]
public static Task<string> ReceiveByteArray(byte[] receivedBytes)
{
return Task.FromResult(
Encoding.UTF8.GetString(receivedBytes, 0, receivedBytes.Length));
}
}
.NET から JavaScript を呼び出すときのバイト配列の使用については、「ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出す」をご覧ください。
JavaScript から .NET へのストリーム
Blazor では、JavaScript から .NET に直接データをストリーミングすることがサポートされます。 ストリームは Microsoft.JSInterop.IJSStreamReference
インターフェイスを使用して要求されます。
Microsoft.JSInterop.IJSStreamReference.OpenReadStreamAsync
では Stream が返され、次のパラメーターが使用されます。
maxAllowedSize
: JavaScript からの読み取り操作に許可される最大バイト数。指定されていない場合、既定では 512,000 バイトになります。cancellationToken
: 読み取りをキャンセルするための CancellationToken。
JavaScript の場合:
function streamToDotNet() {
return new Uint8Array(10000000);
}
C# コードの場合:
var dataReference =
await JS.InvokeAsync<IJSStreamReference>("streamToDotNet");
using var dataReferenceStream =
await dataReference.OpenReadStreamAsync(maxAllowedSize: 10_000_000);
var outputPath = Path.Combine(Path.GetTempPath(), "file.txt");
using var outputFileStream = File.OpenWrite(outputPath);
await dataReferenceStream.CopyToAsync(outputFileStream);
前の例の場合:
JS
は、挿入された IJSRuntime インスタンスです。 IJSRuntime は Blazor フレームワークによって登録されます。dataReferenceStream
は、現在のユーザーの一時フォルダー パス (GetTempPath) でディスク (file.txt
) に書き込まれます。
「ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出す」では、逆の動作である、DotNetStreamReference を使用した .NET から JavaScript へのストリーミングについて説明しています。
「ASP.NET Core Blazor ファイルのアップロード」では、Blazor でファイルをアップロードする方法を説明しています。 サーバー側コンポーネントで <textarea>
データをストリーミングするフォームの例については、「ASP.NET Core Blazor フォームのトラブルシューティング」を参照してください。
JavaScript [JSImport]
/[JSExport]
相互運用
このセクションはクライアント側コンポーネントに適用されます。
IJSRuntime インターフェイスに基づく Blazor の JS 相互運用メカニズムを使用してクライアント側コンポーネントで JavaScript (JS) と対話する代わりに、.NET 7 以降を対象とするアプリで JS[JSImport]
/[JSExport]
相互運用 API を使用できます。
詳細については、「ASP.NET Core Blazor を使用した JavaScript JSImport/JSExport 相互運用」をご覧ください。
JavaScript 相互運用オブジェクト参照の破棄
JavaScript (JS) の相互運用に関する記事全体の例では、一般的なオブジェクト破棄パターンが示されています。
この記事で説明されているように、JS から .NET を呼び出すときは、.NET メモリのリークを避けるため、.NET または JS から、作成された DotNetObjectReference を破棄します。
「ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出す」で説明されているように、.NET から JS を呼び出すときは、JS メモリのリークを避けるため、.NET または JS から、作成されたすべての IJSObjectReference/IJSInProcessObjectReference/
JSObjectReference
を破棄します。
JS 相互運用オブジェクト参照は、参照を作成する JS 相互運用呼び出しの側で識別子によってキー付けされたマップとして実装されます。 オブジェクトの破棄が .NET または JS の側から開始されると、Blazor はマップからエントリを削除し、オブジェクトへの強力な参照が他に存在しない限り、オブジェクトをガベージ コレクションできます。
.NET マネージド メモリのリークを回避するため、少なくとも、.NET 側で作成されたオブジェクトを常に破棄します。
コンポーネントの破棄中の DOM クリーンアップ タスク
詳しくは、「ASP.NET Core Blazor JavaScript の相互運用性 (JS 相互運用)」をご覧ください。
回線を使用しない JavaScript 相互運用呼び出し
詳しくは、「ASP.NET Core Blazor JavaScript の相互運用性 (JS 相互運用)」をご覧ください。
その他のリソース
- ASP.NET Core で .NET メソッドから JavaScript 関数を呼び出すBlazor
InteropComponent.razor
の例 (dotnet/AspNetCore
GitHub リポジトリのmain
ブランチ):main
ブランチは、ASP.NET Core の製品単位の現在の開発を表します。 別のリリースのブランチ (release/{VERSION}
、{VERSION}
プレースホルダーがリリース バージョンなど) を選択するには、 Switch のブランチまたはタグ ドロップダウン リストを使用してブランチを選択します。 存在しなくなったブランチの場合は、 Tags タブを使用して API (v7.0.0
など) を検索します。- DOM の操作
- Blazor サンプル GitHub リポジトリ (
dotnet/blazor-samples
) (ダウンロード方法) - ASP.NET Core Blazor アプリのエラーを処理する (「JavaScript 相互運用」のセクション)
- 脅威の緩和: ブラウザーから呼び出される .NET メソッド
ASP.NET Core