Вызов методов .NET из функций JavaScript в ASP.NET Core Blazor
Примечание.
Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 8 этой статьи.
Предупреждение
Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в статье о политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 8 этой статьи.
Внимание
Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
В текущем выпуске см . версию .NET 8 этой статьи.
В этой статье объясняется, как вызывать методы .NET в JavaScript (JS).
Сведения о том, как вызывать функции JS из .NET, смотрите в статье Вызов функций JavaScript из методов .NET в ASP.NET Core Blazor.
Вызов статического метода .NET
Чтобы вызвать статический метод .NET в JavaScript (JS), воспользуйтесь функциями 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
(клиентские компоненты) возвращает результат операции.
Внимание
Для компонентов на стороне сервера рекомендуется асинхронная функция (invokeMethodAsync
) для синхронной версии (invokeMethod
).
Метод .NET должен быть открытым, статическим и иметь атрибут [JSInvokable]
.
В следующем примере :
- Заполнитель
{<T>}
указывает тип возвращаемого значения, который требуется только для методов, возвращающих значение. - Заполнитель
{.NET METHOD ID}
является идентификатором метода.
@code {
[JSInvokable]
public static Task{<T>} {.NET METHOD ID}()
{
...
}
}
Примечание.
Вызов открытых универсальных методов не поддерживается со статическими методами .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)
{
await module.DisposeAsync();
}
}
}
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 компонента, который записывает результат в консоль средств веб-разработчика браузера. 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)
{
await module.DisposeAsync();
}
}
}
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 компонента, который записывает результат в консоль средств веб-разработчика браузера. 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-атрибут onclick
элемента <button>
— это назначение обработчика событий JavaScript onclick
для обработки событий click
, а не атрибут @onclick
директивы Blazor. Функция returnArrayAsync
JS назначается в качестве обработчика.
returnArrayAsync
JS Следующая функция вызывает ReturnArrayAsync
метод .NET компонента, который записывает результат в консоль средств веб-разработчика браузера. BlazorSample
— это имя сборки приложения.
<script>
window.returnArrayAsync = () => {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
.then(data => {
console.log(data);
});
};
</script>
Примечание.
Общие рекомендации по 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
ReturnArrayAsync
.NET:
DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
DotNet.invokeMethod('BlazorSample', 'DifferentMethodName');
(только клиентские компоненты)
Примечание.
Пример метода 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 });
Дополнительные сведения см. в статье Асинхронное программирование с использованием ключевых слов async и await руководства по C#.
Создание объектов JavaScript и ссылок на данные для передачи в .NET
Вызовите DotNet.createJSObjectReference(jsObject)
ссылку на JS объект, чтобы она была передана в .NET, где jsObject
используется JS Object
для создания ссылки на JS объект. В следующем примере передается ссылка на несериализируемый window
объект в .NET, который получает его в методе ReceiveWindowObject
C# в виде IJSObjectReference:
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', 'ReceiveWindowObject',
DotNet.createJSObjectReference(window));
[JSInvokable]
public static void ReceiveWindowObject(IJSObjectReference objRef)
{
...
}
В предыдущем примере {ASSEMBLY NAME}
заполнитель — это пространство имен приложения.
Примечание.
В предыдущем примере не требуется удаление JSObjectReference
объекта, так как ссылка на window
объект не содержится JS.
Для поддержания ссылки на ссылку на нее JSObjectReference
требуется удалить ее, чтобы избежать утечки JS памяти на клиенте. В следующем примере выполняется рефакторинг предыдущего кода для записи ссылки на нее JSObjectReference
, а затем вызов DotNet.disposeJSObjectReference()
для удаления ссылки:
var jsObjectReference = DotNet.createJSObjectReference(window);
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', 'ReceiveWindowObject', jsObjectReference);
DotNet.disposeJSObjectReference(jsObjectReference);
В предыдущем примере {ASSEMBLY NAME}
заполнитель — это пространство имен приложения.
Вызов для DotNet.createJSStreamReference(streamReference)
создания ссылки на поток таким образом, чтобы он был передан в .NET, где streamReference
находится ArrayBuffer
Blob
или любой типизированный массив, например Uint8Array
или Float32Array
используемый для создания ссылки на JS JS поток.
Вызов метода .NET экземпляра
Чтобы вызвать метод .NET экземпляра из JavaScript (JS), выполните приведенные ниже действия.
Передайте экземпляр .NET по ссылке JS, заключив в оболочку экземпляр в объект DotNetObjectReference и вызвав для него метод Create.
Вызов метода экземпляра .NET из JS
invokeMethodAsync
переданного DotNetObjectReferenceкомпонента (рекомендуется) илиinvokeMethod
(только на стороне клиента). Передайте идентификатор метода .NET экземпляра и все аргументы. Экземпляр .NET можно также передать в качестве аргумента при вызове других методов .NET из JS.В следующем примере :
dotNetHelper
имеет значение DotNetObjectReference.- Заполнитель
{.NET METHOD ID}
является идентификатором метода .NET. - Заполнитель
{ARGUMENTS}
является необязательными, разделенными запятыми аргументами для передачи в метод, каждый из которых должен быть сериализуемым в формате JSON.
dotNetHelper.invokeMethodAsync('{.NET METHOD ID}', {ARGUMENTS});
Примечание.
invokeMethodAsync
иinvokeMethod
не принимаю параметр имени сборки при вызове метода экземпляра.invokeMethodAsync
JSPromise
возвращает результат операции.invokeMethod
(только клиентские компоненты) возвращает результат операции.Внимание
Для компонентов на стороне сервера рекомендуется асинхронная функция (
invokeMethodAsync
) для синхронной версии (invokeMethod
).Удалите DotNetObjectReference.
В следующих разделах этой статьи описываются различные подходы к вызову метода экземпляра .NET:
Избегайте обрезки вызываемых методов JavaScript .NET
Этот раздел относится к клиентским приложениям с включенной компиляцией (AOT) и повторной связью среды выполнения.
Несколько примеров в следующих разделах основаны на подходе к экземпляру класса, где метод JavaScript, вызывающий .NET, [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).
Следующая функция JS sayHello1
получает DotNetObjectReference и вызывает invokeMethodAsync
для вызова метода .NET GetHelloMessage
компонента.
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Примечание.
Общие рекомендации по 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
задав JSonclick
свойство, а не Blazor@onclick
атрибут директивы.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)
{
await module.DisposeAsync();
}
dotNetHelper?.Dispose();
}
}
В предыдущем примере:
JS
является внедренным экземпляром IJSRuntime. IJSRuntime регистрируется платформой Blazor.- Имя переменной
dotNetHelper
является произвольным и может быть изменено на любое желаемое имя. - Компонент должен явным образом удалить DotNetObjectReference, чтобы разрешить сбор мусора и предотвратить утечку памяти.
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
является произвольным и может быть изменено на любое желаемое имя.
Примечание.
Общие рекомендации по 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 Core Blazor и Ведение журнала в .NET Core и ASP.NET Core.
Примечание.
Открытые универсальные типы и методы не указывают типы для заполнителей. Напротив, закрытые универсальные шаблоны предоставляют типы для всех заполнителей. В примерах этого раздела описываются закрытые универсальные шаблоны, но при этом поддерживается и вызов методов экземпляра взаимодействия с 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 void 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
, вызывается только асинхронная функцияinvokeMethodAsync
, так как JS взаимодействие выполняется в серверном компоненте. - Для демонстрации вызов функции 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>
Примечание.
Общие рекомендации по 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: 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
с которыми не выполняются. Для компонентов на стороне сервера рекомендуется асинхронная функция (invokeMethodAsync
) для синхронной версии (invokeMethod
).
Типичные выходные данные компонента на стороне сервера:
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:
- вызывает метод .NET
GetHelloMessage
для переданного метода DotNetObjectReference; - возвращает сообщение из
GetHelloMessage
вызывающему объектуsayHello1
.
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Примечание.
Общие рекомендации по JS расположению и нашим рекомендациям для рабочих приложений см. в расположении JavaScript в приложениях ASP.NET CoreBlazor.
В предыдущем примере имя переменной dotNetHelper
является произвольным и может быть изменено на любое желаемое имя.
Следующий класс HelloHelper
имеет вызываемый JS метод .NET с именем GetHelloMessage
. При создании 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}!";
}
Метод CallHelloHelperGetHelloMessage
в следующем классе JsInteropClasses3
вызывает функцию JS sayHello1
с новым экземпляром 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
синтаксисом.
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!
при указании имени Amy Pond
в name
поле.
В предыдущем компоненте ссылка на объект .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
методUpdateMessage
ListItem1
изменяет текст элемента списка и скрывает кнопку.
Следующий класс 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();
}
}
Следующая функция updateMessageCaller
JS вызывает метод .NET UpdateMessageCaller
.
<script>
window.updateMessageCaller = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('UpdateMessageCaller');
dotNetHelper.dispose();
}
</script>
Примечание.
Общие рекомендации по 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
выбран, этот методListItem2
UpdateMessage
изменяет текст элемента списка в первом<span>
и скрывает второй<span>
, задавая для его свойстваdisplay
значениеnone
.
Следующая assignDotNetHelper
JS функция назначает DotNetObjectReference элементу в свойстве с именем dotNetHelper
. Следующая interopCall
JS функция использует 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>
Примечание.
Общие рекомендации по JS расположению и нашим рекомендациям для рабочих приложений см. в расположении JavaScript в приложениях ASP.NET CoreBlazor.
В предыдущем примере имя переменной dotNetHelper
является произвольным и может быть изменено на любое желаемое имя.
Следующий компонент ListItem2
является общим компонентом, который можно использовать любое количество раз в родительском компоненте. Он создает элементы списка (<li>...</li>
) для списка HTML (<ul>...</ul>
или <ol>...</ol>
).
Каждый экземпляр компонента ListItem2
вызывает функцию assignDotNetHelper
JS в OnAfterRenderAsync
со ссылкой на элемент (первый элемент <span>
в элементе списка) и экземпляром компонента как DotNetObjectReference.
Когда выбрано сообщение компонента ListItem2
<span>
, вызывается interopCall
для передачи элемента <span>
в качестве параметра (this
), который вызывает метод .NET UpdateMessage
. В UpdateMessage
StateHasChanged
вызывается для обновления пользовательского интерфейса, когда задается 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 void 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)
{
await module.DisposeAsync();
}
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 void 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)
{
await module.DisposeAsync();
}
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:
- Загрузите скрипт в разметку элемента
<head>
(обычно не рекомендуется). - Загрузите скрипт в разметку элемента
<body>
. - Загрузите скрипт из внешнего файла JavaScript (
.js
), размещенного совместно с компонентом - Загрузите скрипт из внешнего файла JavaScript (
.js
) - Внедрение скрипта до или после Blazor запуска
Использование JS модулей для загрузки JS описано в этой статье в разделе изоляции JavaScript в модулях JavaScript.
Предупреждение
Только поместите <script>
тег в файл компонента (.razor
), если компонент гарантированно принимает статическую отрисовку на стороне сервера (статический SSR), так как <script>
тег не может быть динамически обновлен.
Предупреждение
Не помещайте тег <script>
в файл компонента (.razor
), так как тег <script>
не может изменяться динамически.
Изоляция JavaScript в модулях JavaScript
Blazor реализует изоляцию JavaScript (JS) в стандартных модулях JavaScript (см. спецификацию ECMAScript). Загрузка модуля JavaScript работает так же Blazor , как и для других типов веб-приложений, и вы можете настроить способ определения модулей в приложении. Руководство по использованию модулей JavaScript см . в веб-документации MDN: модули 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.
Укажите функцию JS sendByteArray
. Функция вызывается статически, которая включает параметр имени сборки в 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>
Примечание.
Общие рекомендации по 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)
{
await module.DisposeAsync();
}
}
}
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)
{
await module.DisposeAsync();
}
}
}
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 Основных Blazor форм".
Взаимодействие JavaScript [JSImport]
/[JSExport]
Этот раздел относится к клиентским компонентам.
В качестве альтернативы взаимодействию с JavaScript (JS) в клиентских компонентах с помощью BlazorJS механизма взаимодействия на IJSRuntime основе интерфейса/JS[JSImport]
[JSExport]
API взаимодействия доступен для приложений, предназначенных для .NET 7 или более поздней версии.
Дополнительные сведения см. в статье JavaScript JSImport/JSExport interop with ASP.NET Core Blazor.
Удаление ссылок на объекты взаимодействия JavaScript
Примеры в статьях взаимодействия JavaScript (JS) демонстрируют типичные шаблоны удаления объектов:
При вызове .NET из JS,как описано в этой статье, удалите созданный DotNetObjectReference из .NET или из JS нее утечку памяти .NET.
При вызове из .NET, как описано в описании вызовов JS функций JavaScript из методов .NET в ASP.NET CoreBlazor, удалите все созданныеIJSInProcessObjectReferenceIJSObjectReference//
JSObjectReference
из .NET или из JS этого, чтобы избежать утечки JS памяти.
JS Ссылки на объекты взаимодействия реализуются в виде карты с ключом идентификатора на стороне JS вызова взаимодействия, создающего ссылку. Если удаление объектов инициируется из .NET или JS на стороне, удаляет запись из карты, Blazor а объект может быть собран мусором до тех пор, пока нет другой строгой ссылки на объект.
Как минимум, всегда удалять объекты, созданные на стороне .NET, чтобы избежать утечки управляемой памяти .NET.
Задачи очистки DOM во время удаления компонентов
Дополнительные сведения см. в разделе Взаимодействие JavaScript приложения Blazor ASP.NET Core (взаимодействие JS).
Вызовы взаимодействия JavaScript без канала
Дополнительные сведения см. в разделе Взаимодействие JavaScript приложения Blazor ASP.NET Core (взаимодействие JS).
Дополнительные ресурсы
- Вызов функций JavaScript из методов .NET в ASP.NET Core Blazor
InteropComponent.razor
пример (dotnet/AspNetCore
ветвь репозиторияmain
GitHub):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