Заметка
Доступ к этой странице требует авторизации. Вы можете попробовать войти в систему или изменить каталог.
Доступ к этой странице требует авторизации. Вы можете попробовать сменить директорию.
Note
Это не последняя версия этой статьи. В текущей версии см. версию .NET 10 этой статьи.
Warning
Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 9 этой статьи.
В этой статье объясняется, как вызывать методы .NET в JavaScript (JS).
Сведения о том, как вызывать функции JS из .NET, смотрите в статье Вызов функций JavaScript из методов .NET в ASP.NET Core Blazor.
Вызов статического метода .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 расположению и нашим рекомендациям для рабочих приложенийBlazor. в расположении JavaScript в приложениях ASP.NET Core.
При нажатии кнопки 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 (только на стороне клиента) вызов метода DifferentMethodNameReturnArrayAsync .NET:
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 });
Дополнительные сведения см. в статье Асинхронное программирование с использованием ключевых слов async и await руководства по C#.
Создание объектов 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объекта, так как ссылка на window объект не содержится JS.
Для поддержания ссылки на ссылку на нее 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) создания ссылки на поток таким образом, чтобы он был передан в .NET, где JS находится streamReferenceArrayBufferили любой Blob, например или Uint8Arrayиспользуемый для создания ссылки на Float32ArrayJS поток.
Вызов метода .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не принимаю параметр имени сборки при вызове метода экземпляра.invokeMethodAsyncJSPromiseвозвращает результат операции.invokeMethod(только клиентские компоненты) возвращает результат операции.Important
Для компонентов на стороне сервера рекомендуется асинхронная функция (
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).
Следующая функция sayHello1JS получает DotNetObjectReference и вызывает invokeMethodAsync для вызова метода .NET GetHelloMessage компонента.
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Note
Общие рекомендации по JS расположению и нашим рекомендациям для рабочих приложенийBlazor. в расположении JavaScript в приложениях ASP.NET Core.
В предыдущем примере имя переменной 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 задав JSJS свойство, а не onclickBlazor атрибут директивы.@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находится в ловушке во время удаления модуля в случае BlazorSignalR потери канала. Если предыдущий код используется в Blazor WebAssembly приложении, нет подключения к потере, SignalR поэтому можно удалить блок и оставить строку, которая удаляет
try-catchмодуль (await module.DisposeAsync();). Дополнительные сведения см. в разделе Взаимодействие JavaScript приложения Blazor ASP.NET Core (взаимодействие JS).
CallDotNetExampleOneHelper.razor.js:
export class GreetingHelpers {
static dotNetHelper;
static setDotNetHelper(value) {
GreetingHelpers.dotNetHelper = value;
}
static async sayHello() {
const msg =
await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetHelloMessage');
alert(`Message from .NET: "${msg}"`);
}
static async welcomeVisitor() {
const msg =
await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetWelcomeMessage');
alert(`Message from .NET: "${msg}"`);
}
}
export function addHandlers() {
const sayHelloBtn = document.getElementById("sayHelloBtn");
sayHelloBtn.addEventListener("click", GreetingHelpers.sayHello);
const welcomeVisitorBtn = document.getElementById("welcomeVisitorBtn");
welcomeVisitorBtn.addEventListener("click", GreetingHelpers.welcomeVisitor);
}
В предыдущем примере имя переменной dotNetHelper является произвольным и может быть изменено на любое желаемое имя.
@page "/call-dotnet-example-one-helper"
@implements IDisposable
@inject IJSRuntime JS
<h1>Pass <code>DotNetObjectReference</code> to a JavaScript class</h1>
<p>
<label>
Message: <input @bind="name" />
</label>
</p>
<p>
<button onclick="GreetingHelpers.sayHello()">
Trigger JS function <code>sayHello</code>
</button>
</p>
<p>
<button onclick="GreetingHelpers.welcomeVisitor()">
Trigger JS function <code>welcomeVisitor</code>
</button>
</p>
@code {
private string? name;
private DotNetObjectReference<CallDotNetExampleOneHelper>? dotNetHelper;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
dotNetHelper = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("GreetingHelpers.setDotNetHelper",
dotNetHelper);
}
}
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
[JSInvokable]
public string GetWelcomeMessage() => $"Welcome, {name}!";
public void Dispose()
{
dotNetHelper?.Dispose();
}
}
В предыдущем примере:
-
JSявляется внедренным экземпляром IJSRuntime. IJSRuntime регистрируется платформой Blazor. - Имя переменной
dotNetHelperявляется произвольным и может быть изменено на любое желаемое имя. - Компонент должен явным образом удалить DotNetObjectReference, чтобы разрешить сбор мусора и предотвратить утечку памяти.
<script>
class GreetingHelpers {
static dotNetHelper;
static setDotNetHelper(value) {
GreetingHelpers.dotNetHelper = value;
}
static async sayHello() {
const msg =
await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetHelloMessage');
alert(`Message from .NET: "${msg}"`);
}
static async welcomeVisitor() {
const msg =
await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetWelcomeMessage');
alert(`Message from .NET: "${msg}"`);
}
}
window.GreetingHelpers = GreetingHelpers;
</script>
В предыдущем примере:
- Класс
GreetingHelpersдобавляется в объектwindow, чтобы глобально определить класс, разрешающий Blazor находить класс для взаимодействия JS. - Имя переменной
dotNetHelperявляется произвольным и может быть изменено на любое желаемое имя.
Note
Общие рекомендации по JS расположению и нашим рекомендациям для рабочих приложенийBlazor. в расположении JavaScript в приложениях ASP.NET Core.
Вызов универсальных методов классов .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класса универсального типа вызываются с аргументами, представляющими строки и числа. - Клиентские компоненты поддерживают синхронную
invokeMethodсинхронизацию методов .NET.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 расположению и нашим рекомендациям для рабочих приложенийBlazor. в расположении JavaScript в приложениях ASP.NET Core.
Следующий компонент 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 расположению и нашим рекомендациям для рабочих приложенийBlazor. в расположении JavaScript в приложениях ASP.NET Core.
В предыдущем примере имя переменной 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 вызывает функцию 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 синтаксисом.
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метод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 расположению и нашим рекомендациям для рабочих приложенийBlazor. в расположении JavaScript в приложениях ASP.NET Core.
В предыдущем примере имя переменной dotNetHelper является произвольным и может быть изменено на любое желаемое имя.
Следующий компонент ListItem1 является общим компонентом, который можно использовать любое количество раз в родительском компоненте. Он создает элементы списка (<li>...</li>) для списка HTML (<ul>...</ul> или <ol>...</ol>). Каждый экземпляр компонента ListItem1 устанавливает экземпляр MessageUpdateInvokeHelper, а для Action задается метод UpdateMessage.
Если нажата кнопка ListItem1 компонента InteropCall, updateMessageCaller вызывается с созданным объектом DotNetObjectReference для экземпляра MessageUpdateInvokeHelper. Это позволяет платформе вызывать UpdateMessageCaller в этом экземпляре ListItem1 объекта MessageUpdateInvokeHelper. Переданный объект 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 расположению и нашим рекомендациям для рабочих приложенийBlazor. в расположении JavaScript в приложениях ASP.NET Core.
В предыдущем примере имя переменной dotNetHelper является произвольным и может быть изменено на любое желаемое имя.
Следующий компонент ListItem2 является общим компонентом, который можно использовать любое количество раз в родительском компоненте. Он создает элементы списка (<li>...</li>) для списка HTML (<ul>...</ul> или <ol>...</ol>).
Каждый экземпляр компонента ListItem2 вызывает функцию assignDotNetHelperJS в OnAfterRenderAsync со ссылкой на элемент (первый элемент <span> в элементе списка) и экземпляром компонента как DotNetObjectReference.
Когда выбрано сообщение компонента ListItem2<span>, вызывается 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:
-
Загрузите скрипт в разметку элемента
<head>(обычно не рекомендуется). -
Загрузите скрипт в разметку элемента
<body>. -
Загрузите скрипт из внешнего файла JavaScript (
.js), размещенного совместно с компонентом -
Загрузите скрипт из внешнего файла JavaScript (
.js) - Внедрение скрипта до или после Blazor запуска
Использование JS модулей для загрузки JS описано в этой статье в разделе изоляции JavaScript в модулях JavaScript.
Warning
Только поместите <script> тег в файл компонента (.razor), если компонент гарантированно принимает статическую отрисовку на стороне сервера (статический SSR), так как <script> тег не может быть динамически обновлен.
Warning
Не помещайте тег <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.
Укажите функцию 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>
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 Основных 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
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ветвь репозиторияmainGitHub):mainветвь представляет текущую разработку подразделения продукта для следующего выпуска ASP.NET Core. Чтобы выбрать ветвь для другого выпуска (например,release/{VERSION}где{VERSION}заполнитель является версией выпуска), используйте раскрывающийся список переключателей или тегов , чтобы выбрать ветвь. Для ветви, которая больше не существует, используйте вкладку "Теги ", чтобы найти API (например,v7.0.0). - Взаимодействие с DOM
-
Blazorпримеры репозитория GitHub (
dotnet/blazor-samplesкак скачать) - Blazor ASP.NET Core (раздел взаимодействия JavaScript)
- Устранение угроз: методы .NET, вызываемые из браузера