Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Note
Это не последняя версия этой статьи. В текущей версии см. версию .NET 10 этой статьи.
Warning
Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. В текущей версии см. версию .NET 10 этой статьи.
В этой статье объясняется, как вызывать методы .NET в JavaScript (JS).
Сведения о том, как вызывать функции JS из .NET, смотрите в статье Вызов функций JavaScript из методов .NET в ASP.NET Core Blazor.
Note
Поддерживаются типы объединения C#.
Вызов статического метода .NET
Чтобы вызвать статический метод .NET в JavaScript (JS), воспользуйтесь функциями JS:
-
DotNet.invokeMethodAsync(рекомендуется): асинхронный для компонентов на стороне сервера и на стороне клиента. -
DotNet.invokeMethod: синхронный только для клиентских компонентов.
Передайте имя сборки, содержащей метод, идентификатор статического метода .NET и любые аргументы.
В следующем примере :
- Заполнитель
{PACKAGE ID/ASSEMBLY NAME}— это идентификатор пакета проекта (<PackageId>в файле проекта) для имени библиотеки или сборки для приложения. - Заполнитель
{.NET METHOD ID}является идентификатором метода .NET. - Заполнители
{ARGUMENTS}— это необязательные аргументы, разделённые запятыми и передаваемые в метод, каждый из которых должен допускать сериализацию в JSON.
DotNet.invokeMethodAsync('{PACKAGE ID/ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});
DotNet.invokeMethodAsync возвращает JS Promise, представляющий результат операции.
DotNet.invokeMethod (клиентские компоненты) возвращает результат операции.
Important
Для компонентов на стороне сервера рекомендуется асинхронная функция (invokeMethodAsync) для синхронной версии (invokeMethod).
Метод .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);
}
Функция addHandlersJS добавляет click событие в кнопку. Функция returnArrayAsyncJS назначается в качестве обработчика.
Функция returnArrayAsyncJS вызывает ReturnArrayAsync метод .NET компонента, который записывает результат в консоль средств веб-разработчика браузера.
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);
}
Функция addHandlersJS добавляет click событие в кнопку. Функция returnArrayAsyncJS назначается в качестве обработчика.
Функция returnArrayAsyncJS вызывает ReturnArrayAsync метод .NET компонента, который записывает результат в консоль средств веб-разработчика браузера.
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 });
}
}
HTML-атрибут <button> элемента onclick — это назначение обработчика событий JavaScript onclick для обработки событий click, а не атрибут Blazor директивы @onclick. Функция returnArrayAsyncJS назначается в качестве обработчика.
returnArrayAsync
JS Следующая функция вызывает ReturnArrayAsync метод .NET компонента, который записывает результат в консоль средств веб-разработчика браузера.
BlazorSample — это имя сборки приложения.
<script>
window.returnArrayAsync = () => {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
.then(data => {
console.log(data);
});
};
</script>
Note
Общие рекомендации о расположении JS и наши рекомендации для рабочих приложений см. в статье Расположение JavaScript в приложениях ASP.NET CoreBlazor.
При выборе кнопки Trigger .NET static method в консоли инструментов разработчика браузера отображаются данные массива. Формат выходных данных незначительно различается в разных браузерах. Следующий вывод показывает формат, используемый в Microsoft Edge:
Array(3) [ 11, 12, 13 ]
Передайте данные в метод .NET при вызове invokeMethodAsync функции путем передачи данных в качестве аргументов.
Чтобы продемонстрировать передачу данных в .NET, передайте начальную позицию методу ReturnArrayAsync в том месте, где этот метод вызывается в JS:
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 ]
Идентификатор метода .NET для JS вызова — это имя метода .NET, но можно указать другой идентификатор с помощью конструктора [JSInvokable] атрибутов . В следующем примере DifferentMethodName — это назначенный идентификатор для метода ReturnArrayAsync:
[JSInvokable("DifferentMethodName")]
В вызове DotNet.invokeMethodAsync (серверные или клиентские компоненты) или DotNet.invokeMethod (только клиентские компоненты) вызовите DifferentMethodName, чтобы выполнить метод .NET ReturnArrayAsync:
DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');-
DotNet.invokeMethod('BlazorSample', 'DifferentMethodName');(только клиентские компоненты)
Note
Пример метода ReturnArrayAsync в этом разделе возвращает результат Task без использования явных ключевых слов C# (async и await). Методы программирования с помощью async и await являются типичными для методов, которые используют ключевое слово await для возврата значений асинхронных операций.
Метод ReturnArrayAsync, состоящий из ключевых слов async и await:
[JSInvokable]
public static async Task<int[]> ReturnArrayAsync() =>
await Task.FromResult(new int[] { 11, 12, 13 });
Дополнительные сведения см. в руководстве по C# в разделе Асинхронное программирование с использованием ключевых слов async и await.
Создание объектов JavaScript и ссылок на данные для передачи в .NET
Вызовите DotNet.createJSObjectReference(jsObject), чтобы создать ссылку на объект JS, чтобы её можно было передать в .NET, где jsObject — это JS Object, используемый для создания ссылки на объект JS. В следующем примере передается ссылка на несериализируемый window объект в .NET, который получает его в методе ReceiveWindowObject C# в виде IJSObjectReference:
DotNet.invokeMethodAsync('{PACKAGE ID/ASSEMBLY NAME}', 'ReceiveWindowObject',
DotNet.createJSObjectReference(window));
[JSInvokable]
public static void ReceiveWindowObject(IJSObjectReference objRef)
{
...
}
В предыдущем примере {PACKAGE ID/ASSEMBLY NAME} является заполнителем и представляет собой идентификатор пакета проекта (<PackageId> в файле проекта) для имени библиотеки или сборки приложения.
Note
В предыдущем примере не требуется освобождать JSObjectReference, так как в JS не хранится ссылка на объект window.
Сохраняя ссылку на JSObjectReference, необходимо освободить её, чтобы избежать утечки памяти JS на стороне клиента. В следующем примере предыдущий код переработан так, чтобы сохранить ссылку на JSObjectReference, а затем вызвать DotNet.disposeJSObjectReference() для освобождения этой ссылки:
var jsObjectReference = DotNet.createJSObjectReference(window);
DotNet.invokeMethodAsync('{PACKAGE ID/ASSEMBLY NAME}', 'ReceiveWindowObject', jsObjectReference);
DotNet.disposeJSObjectReference(jsObjectReference);
В предыдущем примере {PACKAGE ID/ASSEMBLY NAME} является заполнителем и представляет собой идентификатор пакета проекта (<PackageId> в файле проекта) для имени библиотеки или сборки приложения.
Вызовите DotNet.createJSStreamReference(streamReference), чтобы создать ссылку на поток JS, чтобы её можно было передать в .NET, где streamReference — это ArrayBuffer, Blob или любой типизированный массив, например Uint8Array или Float32Array, используемые для создания ссылки на поток JS.
Вызвать метод экземпляра .NET
Чтобы вызвать метод экземпляра .NET из JavaScript (JS):
Передайте экземпляр .NET по ссылке JS, заключив в оболочку экземпляр в объект DotNetObjectReference и вызвав для него метод Create.
Вызов метода экземпляра .NET из JS с помощью
invokeMethodAsync(рекомендуется) илиinvokeMethod(только для компонентов на стороне клиента) из переданного DotNetObjectReference. Передайте идентификатор метода экземпляра .NET и все аргументы. Экземпляр .NET можно также передать в качестве аргумента при вызове других методов .NET из JS.В следующем примере :
-
dotNetHelper— это DotNetObjectReference. - Заполнитель
{.NET METHOD ID}является идентификатором метода .NET. - Заполнители
{ARGUMENTS}— это необязательные аргументы, разделённые запятыми и передаваемые в метод, каждый из которых должен допускать сериализацию в JSON.
dotNetHelper.invokeMethodAsync('{.NET METHOD ID}', {ARGUMENTS});Note
invokeMethodAsyncиinvokeMethodне принимаю параметр имени сборки при вызове метода экземпляра.invokeMethodAsyncвозвращает JSPromise, представляющий результат операции.invokeMethod(только клиентские компоненты) возвращает результат операции.Important
Для компонентов на стороне сервера рекомендуется асинхронная функция (
invokeMethodAsync) для синхронной версии (invokeMethod).-
Удалите DotNetObjectReference.
В следующих разделах этой статьи описываются различные подходы к вызову метода экземпляра .NET:
Избегайте удаления методов .NET, вызываемых из JavaScript
Этот раздел относится к клиентским приложениям с включёнными опережающей компиляцией (AOT) и перекомпоновкой во время выполнения.
Несколько примеров в следующих разделах основаны на подходе с использованием экземпляра класса, где метод .NET, вызываемый из JavaScript и помеченный атрибутом [JSInvokable], является методом класса, который не является компонентом 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).
Следующая функция sayHello1JS получает DotNetObjectReference и вызывает invokeMethodAsync для вызова метода .NET GetHelloMessage компонента.
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Note
Общие рекомендации о расположении JS и наши рекомендации для рабочих приложений см. в статье Расположение JavaScript в приложениях ASP.NET CoreBlazor.
В предыдущем примере имя переменной dotNetHelper является произвольным и может быть изменено на любое желаемое имя.
Для следующего компонента :
- Компонент имеет вызываемый JS метод .NET с именем
GetHelloMessage. - При нажатии кнопки
Trigger .NET instance methodфункция JSsayHello1вызывается с помощью метода DotNetObjectReference. -
sayHello1:- Вызывает
GetHelloMessageи получает сообщение о результате. - Возвращает результат сообщения вызывающему методу
TriggerDotNetInstanceMethod.
- Вызывает
- Возвращенное сообщение из
sayHello1вresultотображается пользователю. - Чтобы избежать утечки памяти и разрешить сборку мусора, ссылка на объект .NET, созданная DotNetObjectReference, будет удалена в методе
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 является произвольным и может быть изменено на любое желаемое имя.
Передайте DotNetObjectReference в класс с несколькими функциями JavaScript
В примере этого раздела описывается, как передать DotNetObjectReference в класс JavaScript (JS) с несколькими функциями.
Создайте и передайте DotNetObjectReference из метода OnAfterRenderAsync жизненного цикла в класс JS, чтобы несколько функций могли его использовать. Убедитесь, что код .NET удаляет DotNetObjectReference, как показано в следующем примере.
В следующем компоненте кнопки Trigger JS function вызывают функции JS путём задания свойства JSonclick, а не атрибута директивы Blazor@onclick.
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 блокируется при утилизации модуля в случае потери цепи SignalR у Blazor. Если предыдущий код используется в приложении Blazor WebAssembly, то нет соединения SignalR, которое можно потерять, поэтому можно удалить блок
try-catchи оставить строку, которая освобождает модуль (await module.DisposeAsync();). Дополнительные сведения см. в разделе Взаимодействие JavaScript в ASP.NET Core (JSinterop)Blazor.
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 и наши рекомендации для рабочих приложений см. в статье Расположение JavaScript в приложениях ASP.NET CoreBlazor.
Вызов универсальных методов классов .NET
Функции JavaScript (JS) могут вызывать методы универсальных классов .NET, в которых функция JS вызывает метод .NET универсального класса.
В следующем классе универсального типа (GenericType<TValue>):
- Класс содержит один параметр типа (
TValue) с одним универсальным свойствомValue. - Класс имеет два не универсальных метода,
[JSInvokable]помеченных атрибутом, каждый из которых имеет параметрnewValueуниверсального типа:-
Updateсинхронно обновляет значениеValueизnewValue. -
UpdateAsyncасинхронно обновляет значениеValueсо значенияnewValueпосле создания ожидаемой задачи с помощью Task.Yield, которая при ожидании асинхронно возвращает выполнение в текущий контекст.
-
- Каждый из методов класса записывает тип
TValueи значениеValueв консоль. Запись в консоль необходима исключительно для демонстрации. Приложения в рабочей среде обычно избегают вывода в консоль, предпочитая логирование. Для получения дополнительной информации см. Логирование в ASP.NET CoreBlazor и Логирование в .NET и ASP.NET Core.
Note
Открытые универсальные типы и методы не указывают типы для заполнителей. Напротив, закрытые универсальные шаблоны предоставляют типы для всех заполнителей. Примеры в этом разделе демонстрируют закрытые обобщенные типы, но JS поддерживается вызов методов экземпляра взаимодействия с открытыми обобщенными типами . Использование открытых универсальных шаблонов не поддерживается для вызовов статических методов .NET, описанных ранее в этой статье.
Дополнительные сведения см. в следующих статьях:
- Универсальные классы и методы (документация по C#)
- Универсальные классы (руководство по программированию на C#)
- Обобщения в .NET (документация по .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класса универсального типа вызываются с аргументами, представляющими строки и числа. - Компоненты на стороне клиента поддерживают синхронный вызов методов .NET с помощью
invokeMethod.syncInteropполучает логическое значение, указывающее, выполняется ли взаимодействие JS на стороне клиента. ЕслиsyncInteropпринимает значениеtrue, безопасно вызываетсяinvokeMethod. Если значениеsyncInteropравноfalse, вызывается только асинхронная функцияinvokeMethodAsync, так как JS взаимодействие выполняется в серверном компоненте. - Для демонстрации вызов функции DotNetObjectReference (
invokeMethodилиinvokeMethodAsync), метод .NET (UpdateилиUpdateAsync) и аргумент записываются в консоль. Аргументы используют случайное число, чтобы сопоставить вызов функции JS с вызовом метода .NET (который также записывается в консоль со стороны .NET). Рабочий код обычно не записывается в консоль ни со стороны клиента, ни со стороны сервера. Приложения в рабочей среде обычно используют ведение журналов приложений. Для получения дополнительной информации см. Логирование в ASP.NET CoreBlazor и Логирование в .NET и 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 и наши рекомендации для рабочих приложений см. в статье Расположение JavaScript в приложениях ASP.NET CoreBlazor.
В следующем компоненте GenericsExample:
- Функция JS
invokeMethodsAsyncвызывается при нажатии кнопкиInvoke Interop. - Создается пара типов DotNetObjectReference, а затем передается в функцию JS для экземпляров
GenericTypeв качествеstringиint.
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: обновление: GenericType<System.String>: строка 37802
JS: invokeMethodAsync:UpdateAsync('string 53051')
JS: invokeMethod:Update('string 26784')
.NET: обновление: GenericType<System.String>: строка 26784
JS: invokeMethodAsync:Update(14107)
.NET: Обновление: GenericType<System.Int32>: 14107
JS: invokeMethodAsync:UpdateAsync(48995)
JS: invokeMethod:Update(12872)
.NET: обновление: GenericType<System.Int32>: 12872
.NET: UpdateAsync: GenericType<System.String>: строка 53051
.NET: UpdateAsync: GenericType<System.Int32>: 48995
Если приведённый выше пример реализован в серверном компоненте, синхронных вызовов с invokeMethod удаётся избежать. Для компонентов на стороне сервера рекомендуется асинхронная функция (invokeMethodAsync) для синхронной версии (invokeMethod).
Типичные выходные данные компонента на стороне сервера:
JS: invokeMethodAsync:Update('string 34809')
.NET: обновление: GenericType<System.String>: строка 34809
JS: invokeMethodAsync:UpdateAsync('string 93059')
JS: invokeMethodAsync:Update(41997)
.NET: Обновление: GenericType<System.Int32>: 41997
JS: invokeMethodAsync:UpdateAsync(24652)
.NET: UpdateAsync: GenericType<System.String>: строка 93059
.NET: UpdateAsync: GenericType<System.Int32>: 24652
В предыдущем примере выходных данных показано, что асинхронные методы выполняются в произвольном порядке в зависимости от ряда факторов, включая расписание потоков и скорость выполнения методов. Невозможно надежно предсказать порядок завершения асинхронных вызовов методов.
Примеры экземпляров класса
Следующая функция sayHello1JS:
- вызывает метод .NET
GetHelloMessageдля переданного метода DotNetObjectReference; - возвращает сообщение из
GetHelloMessageвызывающему объектуsayHello1.
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Note
Общие рекомендации о расположении JS и наши рекомендации для рабочих приложений см. в статье Расположение JavaScript в приложениях ASP.NET CoreBlazor.
В предыдущем примере имя переменной dotNetHelper является произвольным и может быть изменено на любое желаемое имя.
Следующий класс HelloHelper имеет вызываемый JS метод .NET с именем GetHelloMessage. При создании HelloHelper для возврата сообщения из GetHelloMessage используется имя, указанное в свойстве Name.
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}!";
}
Метод CallHelloHelperGetHelloMessage в следующем классе JsInteropClasses3 вызывает функцию JSsayHello1 с новым экземпляром HelloHelper.
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);
}
}
Чтобы избежать утечки памяти и обеспечить сборку мусора, ссылка на объект .NET, созданная с помощью DotNetObjectReference, освобождается, когда ссылка на объект выходит из области видимости при использовании синтаксиса using var syntax.
При выборе кнопки Trigger .NET instance method в следующем компоненте вызывается JsInteropClasses3.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);
}
}
}
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);
}
}
На следующем изображении показан отображаемый компонент с именем Amy Pond в поле Name. После нажатия кнопки в пользовательском интерфейсе отобразится 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);
}
}
Чтобы избежать утечки памяти и обеспечить сборку мусора, ссылка на объект .NET, созданная с помощью DotNetObjectReference, освобождается, когда ссылка на объект выходит из области видимости при использовании синтаксиса using var.
Результат, отображаемый компонентом, имеет вид Hello, Amy Pond!, когда в поле name указано имя Amy Pond.
В предыдущем компоненте ссылка на объект .NET удалена. Если класс или компонент не удаляет объект DotNetObjectReference, удалите его из клиента, вызвав метод dispose в переданном объекте DotNetObjectReference:
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методListItem1UpdateMessageизменяет текст элемента списка и скрывает кнопку.
Следующий класс MessageUpdateInvokeHelper поддерживает вызываемый JS метод .NET (UpdateMessageCaller) для вызова объекта Action, указанного при создании экземпляра класса.
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();
}
}
Следующая функция updateMessageCallerJS вызывает метод .NET UpdateMessageCaller.
<script>
window.updateMessageCaller = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('UpdateMessageCaller');
dotNetHelper.dispose();
}
</script>
Note
Общие рекомендации о расположении JS и наши рекомендации для рабочих приложений см. в статье Расположение JavaScript в приложениях ASP.NET CoreBlazor.
В предыдущем примере имя переменной dotNetHelper является произвольным и может быть изменено на любое желаемое имя.
Следующий компонент ListItem1 является общим компонентом, который можно использовать любое количество раз в родительском компоненте. Он создает элементы списка (<li>...</li>) для списка HTML (<ul>...</ul> или <ol>...</ol>). Каждый экземпляр компонента ListItem1 устанавливает экземпляр MessageUpdateInvokeHelper, а для Action задается метод UpdateMessage.
Когда выбрана кнопка InteropCall компонента ListItem1, updateMessageCaller вызывается с созданным объектом DotNetObjectReference для экземпляра MessageUpdateInvokeHelper. Это позволяет фреймворку вызывать UpdateMessageCaller для экземпляра MessageUpdateInvokeHelper этого ListItem1. Переданный объект 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();
}
}
StateHasChanged вызывается для обновления пользовательского интерфейса, если параметр message имеет значение UpdateMessage. Если метод StateHasChanged не вызывается, Blazor не может узнать, что пользовательский интерфейс должен быть обновлен при вызове метода Action.
Следующий родительский компонент содержит четыре элемента списка, каждый из которых является экземпляром компонента ListItem1.
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>
На следующем рисунке показан отрисованный родительский компонент после выбора второй InteropCall кнопки:
- Второй компонент
ListItem1выводит сообщениеUpdateMessage Called!. - Кнопка
InteropCallдля второго компонентаListItem1не отображается, так как свойство CSS для кнопкиdisplayимеет значениеnone.
Метод .NET экземпляра компонента, вызываемый из DotNetObjectReference, назначенный свойству элемента
Назначение DotNetObjectReference свойству HTML-элемента позволяет вызывать методы .NET в экземпляре компонента:
- Ссылка на элемент записывается (ElementReference).
- В методе компонента
OnAfterRender{Async}вызывается функция JavaScript (JS) со ссылкой на элемент и экземпляром компонента как DotNetObjectReference. Функция JS присоединяет DotNetObjectReference к элементу в свойстве. - Когда событие элемента вызывается в JS (например,
onclick), DotNetObjectReference с присоединением к элементу используется для вызова метода .NET.
Подобно подходу, описанному в разделе Вспомогательный класс методов .NET экземпляра компонента, этот подход полезен в случаях, когда нельзя использовать статические методы .NET:
- когда на одной странице отображается несколько компонентов одного типа;
- В серверных приложениях с несколькими пользователями одновременно используется один и тот же компонент.
- Метод .NET вызывается из события JS (например,
onclick), а не из события Blazor (например,@onclick).
В следующем примере :
- Компонент содержит несколько
ListItem2компонентов, которые являются общим компонентом. - Каждый компонент
ListItem2состоит из сообщения элемента списка<span>и второго<span>со свойством CSSdisplay, для которого задано значениеinline-blockдля отображения. - Когда элемент списка компонентов
ListItem2выбран, этот методListItem2UpdateMessageизменяет текст элемента списка в первом<span>и скрывает второй<span>, задавая для его свойстваdisplayзначениеnone.
Следующая assignDotNetHelperJS функция назначает DotNetObjectReference элементу в свойстве с именем dotNetHelper. Следующая функция interopCallJS использует DotNetObjectReference для переданного элемента, чтобы вызвать метод .NET с именем UpdateMessage.
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 и наши рекомендации для рабочих приложений см. в статье Расположение JavaScript в приложениях ASP.NET CoreBlazor.
В предыдущем примере имя переменной dotNetHelper является произвольным и может быть изменено на любое желаемое имя.
Следующий компонент ListItem2 является общим компонентом, который можно использовать любое количество раз в родительском компоненте. Он создает элементы списка (<li>...</li>) для списка HTML (<ul>...</ul> или <ol>...</ol>).
Каждый экземпляр компонента ListItem2 вызывает функцию assignDotNetHelperJS из OnAfterRenderAsync, передавая ссылку на элемент (первый элемент <span> элемента списка) и экземпляр компонента в качестве DotNetObjectReference.
При выборе сообщения <span> компонента ListItem2 вызывается interopCall с передачей элемента <span> в качестве параметра (this), что приводит к вызову метода .NET UpdateMessage. В UpdateMessageStateHasChanged вызывается для обновления пользовательского интерфейса, когда устанавливается message и обновляется свойство display у второго <span>. Если StateHasChanged не вызывается, Blazor не может узнать, что пользовательский интерфейс должен быть обновлен при вызове метода.
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.
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.invokeMethod вместо DotNet.invokeMethodAsync.
Синхронные вызовы возможны, если соблюдаются следующие условия:
Расположение JavaScript
Загрузите код JavaScript (JS) с помощью любого из подходов, описанных в статье о расположении JavaScript:
Использование модулей JS для загрузки JS описано в разделе Изоляция JavaScript в модулях JavaScript этой статьи.
Warning
Помещайте тег <script> в файл компонента (.razor) только в том случае, если гарантировано, что компонент использует статическую отрисовку на стороне сервера (статическая SSR), так как тег <script> нельзя обновлять динамически.
Warning
Не помещайте тег <script> в файл компонента (.razor), так как тег <script> не может изменяться динамически.
Изоляция JavaScript в модулях JavaScript
Blazor реализует изоляцию JavaScript (JS) в стандартных модулях JavaScript (см. спецификацию ECMAScript). Загрузка модулей JavaScript в Blazor происходит так же, как и в других типах веб-приложений, и вы можете настроить способ определения модулей в вашем приложении. Руководство по использованию модулей JavaScript см. в MDN Web Docs: модули JavaScript.
Изоляция JS обеспечивает следующие преимущества:
- Импортированный JS не засоряет глобальное пространство имен.
- Пользователям библиотеки и компонентов не требуется импортировать связанный код JS.
Дополнительные сведения см. в статье Вызов функций JavaScript из методов .NET в ASP.NET Core Blazor.
Динамический импорт с оператором import() поддерживается в ASP.NET Core и Blazor:
if ({CONDITION}) import("/additionalModule.js");
В предыдущем примере заполнитель {CONDITION} представляет собой условную проверку, которая определяет, следует ли загружать модуль.
Сведения о совместимости браузера см. в разделе "Можно ли использовать: модули JavaScript: динамический импорт".
Исключение циклических ссылок на объекты
Объекты, содержащие циклические ссылки, нельзя сериализовать на стороне клиента ни в одном из следующих случаев:
- Вызовы методов .NET.
- Вызовы методов JavaScript из C#, если возвращаемый тип содержит циклические ссылки.
Поддержка массивов байтов
Blazor поддерживает оптимизированное взаимодействие с массивом байтов JavaScript (JS), которое позволяет избежать кодирования и декодирования массивов байтов в Base64. В следующем примере механизм взаимодействия JS используется для передачи массива байтов в .NET.
Укажите функцию sendByteArrayJS. Функция вызывается статически с помощью кнопки в компоненте, с включением параметра имени сборки в вызов 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 и наши рекомендации для рабочих приложений см. в статье Расположение JavaScript в приложениях ASP.NET CoreBlazor.
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));
}
}
Сведения об использовании массива байтов при вызове JavaScript из .NET см. в статье Вызов функций JavaScript из методов .NET в ASP.NET Core Blazor.
Передача потока из 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записывается на диск (file.txt) по пути временной папки текущего пользователя (GetTempPath).
В статье Вызов функций JavaScript из методов .NET в ASP.NET Core Blazor рассматривается обратная операция — потоковая передача данных из .NET в JavaScript с помощью DotNetStreamReference.
В статье Отправка файлов ASP.NET Core Blazor описано, как отправить файл в Blazor. Пример формы, в которой данные <textarea> передаются потоком в компоненте на стороне сервера, см. в разделе Устранение неполадок ASP.NET Core Blazor форм.
Взаимодействие JavaScript [JSImport]/[JSExport]
Этот раздел относится к клиентским компонентам.
В качестве альтернативы взаимодействию с JavaScript (JS) в клиентских компонентах с помощью механизма взаимодействия JS в Blazor, основанного на интерфейсе IJSRuntime, для приложений, ориентированных на .NET 7 или более позднюю версию, доступен API взаимодействия JS[JSImport]/[JSExport].
Дополнительные сведения см. в статье JavaScript JSImport/JSExport interop with ASP.NET Core Blazor.
Удаление ссылок на объекты взаимодействия JavaScript
Во всех статьях о взаимодействии с JavaScript (JS) приведены примеры, демонстрирующие типичные шаблоны освобождения объектов:
При вызове .NET из JS, как описано в этой статье, освобождайте созданный DotNetObjectReference либо из .NET, либо из JS, чтобы избежать утечки памяти .NET.
При вызове JS из .NET, как описано в разделе Вызов функций JavaScript из методов .NET в ASP.NET Core Blazor, освобождайте все созданные IJSObjectReference/IJSInProcessObjectReference/
JSObjectReferenceлибо из .NET, либо из JS, чтобы избежать утечки памяти JS.
JS Ссылки на interop-объекты реализованы в виде словаря, где ключом служит идентификатор на стороне вызова JS interop, создающего ссылку. Когда удаление объекта инициируется со стороны .NET или JS, Blazor удаляет запись из сопоставления, и объект может быть удалён сборщиком мусора, если на него нет других сильных ссылок.
Как минимум, всегда удалять объекты, созданные на стороне .NET, чтобы избежать утечки управляемой памяти .NET.
Задачи очистки DOM во время удаления компонентов
Дополнительные сведения см. в разделе Взаимодействие JavaScript в ASP.NET Core (JSinterop)Blazor.
Вызовы взаимодействия с JavaScript без канала связи
Дополнительные сведения см. в разделе Взаимодействие JavaScript в ASP.NET Core (JSinterop)Blazor.
Дополнительные ресурсы
- Вызов функций JavaScript из методов .NET в ASP.NET Core Blazor
-
InteropComponent.razorпример (dotnet/AspNetCoreветвь репозиторияmainGitHub):mainветвь представляет текущую разработку подразделения продукта для следующего выпуска ASP.NET Core. Чтобы выбрать ветку для другого релиза (например,release/{VERSION}, где{VERSION}— это версия релиза), используйте раскрывающийся список Переключить ветки или теги, чтобы выбрать ветку. Для ветви, которая больше не существует, используйте вкладку "Теги ", чтобы найти API (например,v7.0.0). - Взаимодействие с DOM
-
Blazorпримеры репозитория GitHub (
dotnet/blazor-samplesкак скачать) - Обработка ошибок в приложениях ASP.NET Core Blazor (раздел взаимодействия с JavaScript)
- Устранение угроз: методы .NET, вызываемые из браузера
ASP.NET Core