Chamar métodos .NET de funções JavaScript no ASP.NET Core Blazor
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 9 deste artigo.
Aviso
Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, consulte a Política de Suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.
Importante
Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.
Para a versão atual, consulte a versão .NET 9 deste artigo.
Este artigo explica como invocar os métodos do .NET a partir do JavaScript (JS).
Para obter informações sobre como chamar as funções JS a partir do .NET, confira Chamar funções JavaScript a partir dos métodos do .NET no ASP.NET Core Blazor.
Invocar um método estático do .NET
Para invocar um método estático do .NET a partir do JavaScript (JS), use as funções JS:
DotNet.invokeMethodAsync
(recomendado): assíncrono para componentes do lado do servidor e do cliente.DotNet.invokeMethod
: síncrono apenas para componentes do lado do cliente.
Passe o nome do assembly que contém o método, o identificador do método estático do .NET e quaisquer argumentos.
No exemplo a seguir:
- O espaço reservado
{ASSEMBLY NAME}
é o nome do assembly do aplicativo. - O espaço reservado
{.NET METHOD ID}
é o identificador do método do .NET. - O espaço reservado
{ARGUMENTS}
representa os argumentos opcionais separados por vírgulas a serem passados para o método, dentre os quais, cada um deve ser serializável em JSON.
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});
DotNet.invokeMethodAsync
retorna um JS Promise
que representa o resultado da operação. DotNet.invokeMethod
(componentes do lado do cliente) retorna o resultado da operação.
Importante
Para componentes do lado do servidor, recomendamos a função assíncrona (invokeMethodAsync
) em vez da versão síncrona (invokeMethod
).
O método do .NET deve ser público e estático e deve ter o atributo [JSInvokable]
.
No exemplo a seguir:
- O espaço reservado
{<T>}
indica o tipo de retorno, que só é necessário para métodos que retornam um valor. - O espaço reservado
{.NET METHOD ID}
é o identificador do método.
@code {
[JSInvokable]
public static Task{<T>} {.NET METHOD ID}()
{
...
}
}
Observação
Não há suporte para chamar métodos genéricos abertos com métodos estáticos do .NET, mas há suporte para métodos de instância. Para obter mais informações, confira a seção Chamar métodos de classe genérica do .NET.
No componente a seguir, o método C# ReturnArrayAsync
retorna uma matriz int
. O atributo [JSInvokable]
é aplicado ao método, o que possibilita que o método seja invocador por 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);
}
A funçãoaddHandlers
JS adiciona um eventoclick
ao botão. A função returnArrayAsync
JS é atribuída como o manipulador.
A função returnArrayAsync
JS chama o método do .NET ReturnArrayAsync
do componente, e registra o resultado no console de ferramentas para desenvolvedores Web do navegador. BlazorSample
é o nome de assembly do aplicativo.
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);
}
A funçãoaddHandlers
JS adiciona um eventoclick
ao botão. A função returnArrayAsync
JS é atribuída como o manipulador.
A função returnArrayAsync
JS chama o método do .NET ReturnArrayAsync
do componente, e registra o resultado no console de ferramentas para desenvolvedores Web do navegador. BlazorSample
é o nome de assembly do aplicativo.
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 });
}
}
O atributo HTML <button>
do elemento onclick
é a atribuição do manipulador de eventos onclick
do JavaScript para processar eventos click
, não o atributo de diretiva Blazor do @onclick
. A função returnArrayAsync
JS é atribuída como o manipulador.
A função returnArrayAsync
JS a seguir chama o método do .NET ReturnArrayAsync
do componente e registra o resultado no console de ferramentas para desenvolvedores Web do navegador. BlazorSample
é o nome de assembly do aplicativo.
<script>
window.returnArrayAsync = () => {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
.then(data => {
console.log(data);
});
};
</script>
Observação
Para obter diretrizes gerais sobre o local JS e nossas recomendações para aplicativos de produção, consulte Localização do JavaScript em aplicativos ASP.NET Core Blazor.
Quando o botão Trigger .NET static method
é selecionado, a saída do console de ferramentas de desenvolvedor do navegador exibe os dados da matriz. O formato da saída é um pouco diferente entre os navegadores. A saída a seguir mostra o formato usado pelo Microsoft Edge:
Array(3) [ 11, 12, 13 ]
Passe os dados para um método do .NET ao chamar a função invokeMethodAsync
passando os dados como argumentos.
Para demonstrar a passagem de dados para o .NET, passe uma posição inicial para o ReturnArrayAsync
método em que o método é invocado em 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>
O método invocável ReturnArrayAsync
do componente recebe a posição inicial e cria a matriz com base nele. A matriz é retornada para log no console:
[JSInvokable]
public static Task<int[]> ReturnArrayAsync(int startPosition) =>
Task.FromResult(Enumerable.Range(startPosition, 3).ToArray());
Depois que o aplicativo é recompilado e o navegador é atualizado, a seguinte saída é exibida no console do navegador quando o botão é selecionado:
Array(3) [ 14, 15, 16 ]
O identificador de método do .NET para a chamada JS é o nome do método do .NET, mas você pode especificar um identificador diferente usando o construtor de atributo [JSInvokable]
. No exemplo a seguir, DifferentMethodName
é o identificador de método atribuído para o método ReturnArrayAsync
:
[JSInvokable("DifferentMethodName")]
Na chamada para DotNet.invokeMethodAsync
(componentes do lado do servidor ou do cliente) ou DotNet.invokeMethod
(somente componentes do lado do cliente), chame DifferentMethodName
para executar o método .NET ReturnArrayAsync
:
DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
DotNet.invokeMethod('BlazorSample', 'DifferentMethodName');
(somente componentes do lado do cliente)
Observação
O exemplo de método ReturnArrayAsync
nesta seção retorna o resultado de um Task, sem o uso das palavras-chave explícitas async
e await
C#. Codificar métodos com async
e await
é típico de métodos que usam a palavra-chave await
para retornar o valor de operações assíncronas.
Método ReturnArrayAsync
composto pelas palavras-chave async
e await
:
[JSInvokable]
public static async Task<int[]> ReturnArrayAsync() =>
await Task.FromResult(new int[] { 11, 12, 13 });
Para obter mais informações, confira Programação assíncrona com async e await no guia C#.
Criar referências de dados e objeto JavaScript para passar para o .NET
Chame DotNet.createJSObjectReference(jsObject)
para criar uma referência de objeto JS para que possa ser passada para o .NET, onde jsObject
é o JS Object
usado para criar a referência de objeto JS. O exemplo a seguir passa uma referência ao objeto window
não serializável para o .NET, que o recebe no método C# ReceiveWindowObject
como um IJSObjectReference:
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', 'ReceiveWindowObject',
DotNet.createJSObjectReference(window));
[JSInvokable]
public static void ReceiveWindowObject(IJSObjectReference objRef)
{
...
}
No exemplo anterior, o espaço reservado {ASSEMBLY NAME}
é o namespace do aplicativo.
Observação
O exemplo anterior não requer o descarte do JSObjectReference
, pois uma referência ao objeto window
não é mantida em JS.
Manter uma referência a um JSObjectReference
requer seu descarte para evitar o vazamento de memória JS no cliente. O exemplo a seguir refatora o código anterior para capturar uma referência ao JSObjectReference
, seguido de uma chamada para o DotNet.disposeJSObjectReference()
para descartar a referência:
var jsObjectReference = DotNet.createJSObjectReference(window);
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', 'ReceiveWindowObject', jsObjectReference);
DotNet.disposeJSObjectReference(jsObjectReference);
No exemplo anterior, o espaço reservado {ASSEMBLY NAME}
é o namespace do aplicativo.
Chame DotNet.createJSStreamReference(streamReference)
para criar uma referência de fluxo JS para que possa ser passada para o .NET, onde streamReference
que é um ArrayBuffer
, Blob
ou qualquer matriz de tipo, como Uint8Array
ou Float32Array
, usada para criar a referência de fluxo JS.
Invocar um método de instância do .NET
Para invocar um método de instância do .NET a partir do JavaScript (JS):
Passe a instância do .NET por referência para o JS, encapsulando a instância em um DotNetObjectReference e chamando Create nela.
Invoque um método de instância do .NET a partir de JS usando
invokeMethodAsync
(recomendado) ouinvokeMethod
(somente componentes do lado do cliente) a partir do DotNetObjectReference passado. Passe o identificador do método de instância do .NET e quaisquer argumentos. A instância do .NET também pode ser passada como um argumento ao invocar outros métodos do .NET a partir de JS.No exemplo a seguir:
dotNetHelper
é um DotNetObjectReference.- O espaço reservado
{.NET METHOD ID}
é o identificador do método do .NET. - O espaço reservado
{ARGUMENTS}
representa os argumentos opcionais separados por vírgulas a serem passados para o método, dentre os quais, cada um deve ser serializável em JSON.
dotNetHelper.invokeMethodAsync('{.NET METHOD ID}', {ARGUMENTS});
Observação
invokeMethodAsync
einvokeMethod
não aceitam um parâmetro de nome de assembly ao invocar um método de instância.invokeMethodAsync
retorna um JSPromise
que representa o resultado da operação.invokeMethod
(somente componentes do lado do cliente) retorna o resultado da operação.Importante
Para componentes do lado do servidor, recomendamos a função assíncrona (
invokeMethodAsync
) em vez da versão síncrona (invokeMethod
).Descarte o DotNetObjectReference.
As seções a seguir deste artigo demonstram várias abordagens para invocar um método de instância do .NET:
Evite cortar os métodos do .NET que podem ser invocados por JavaScript
Esta seção se aplica a aplicativos do lado do cliente com compilação AOT e nova vinculação de runtime habilitadas.
Vários dos exemplos nas seções a seguir são baseados em uma abordagem de instância de classe, em que o método do .NET que pode ser invocado por JavaScript marcado com o atributo [JSInvokable]
é um membro de uma classe que não é um componente Razor. Quando esses métodos do .NET estão localizados em um componente Razor, eles são protegidos contra nova vinculação/corte de runtime. Para proteger os métodos do .NET contra o corte fora dos componentes Razor, implemente os métodos com o atributo DynamicDependency
no construtor da classe, como demonstra o exemplo a seguir:
using System.Diagnostics.CodeAnalysis;
using Microsoft.JSInterop;
public class ExampleClass {
[DynamicDependency(nameof(ExampleJSInvokableMethod))]
public ExampleClass()
{
}
[JSInvokable]
public string ExampleJSInvokableMethod()
{
...
}
}
Para obter mais informações, confira Preparar bibliotecas do .NET para corte: DynamicDependency.
Passar um DotNetObjectReference
para uma função JavaScript individual
O exemplo nesta seção demonstra como passar um DotNetObjectReference para uma função JavaScript individual (JS).
A função sayHello1
JS a seguir recebe um DotNetObjectReference e chama o invokeMethodAsync
para chamar o método do .NET GetHelloMessage
de um componente:
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Observação
Para obter diretrizes gerais sobre o local JS e nossas recomendações para aplicativos de produção, consulte Localização do JavaScript em aplicativos ASP.NET Core Blazor.
No exemplo anterior, o nome da variável dotNetHelper
é arbitrário e pode ser alterado para qualquer nome preferencial.
No componente a seguir:
- O componente tem um método do .NET que pode ser invocado por JS chamado
GetHelloMessage
. - Quando o botão
Trigger .NET instance method
é selecionado, a função JS dosayHello1
é chamada com o DotNetObjectReference. sayHello1
:- Chama
GetHelloMessage
e recebe o resultado da mensagem. - Retorna o resultado da mensagem para o método de chamada
TriggerDotNetInstanceMethod
.
- Chama
- A mensagem retornada de
sayHello1
emresult
será exibida para o usuário. - Para evitar uma perda de memória e permitir a coleta de lixo, a referência de objeto do .NET criada por DotNetObjectReference é descartada no método
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();
}
}
No exemplo anterior, o nome da variável dotNetHelper
é arbitrário e pode ser alterado para qualquer nome preferencial.
Use as seguintes diretrizes para passar argumentos para um método de instância:
Adicione parâmetros à invocação do método do .NET. No exemplo a seguir, um nome é passado para o método. Adicione mais parâmetros à lista, conforme necessário.
<script>
window.sayHello2 = (dotNetHelper, name) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage', name);
};
</script>
No exemplo anterior, o nome da variável dotNetHelper
é arbitrário e pode ser alterado para qualquer nome preferencial.
Forneça a lista de parâmetros para o método do .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();
}
}
No exemplo anterior, o nome da variável dotNetHelper
é arbitrário e pode ser alterado para qualquer nome preferencial.
Passar um DotNetObjectReference
para uma classe com várias funções JavaScript
O exemplo nesta seção demonstra como passar um DotNetObjectReference para uma classe JavaScript (JS) com várias funções.
Crie e passe um DotNetObjectReference a partir do método de ciclo de vida OnAfterRenderAsync
para uma classe JS para várias funções usarem. Verifique se o código do .NET descarta o DotNetObjectReference, como mostra o exemplo a seguir.
No componente a seguir, os botões Trigger JS function
chamam as funções JS definindo a propriedade JSonclick
, não o atributo de diretiva @onclick
de Blazor.
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();
}
}
No exemplo anterior:
JS
é uma instância injetada IJSRuntime. IJSRuntime é registrado pela estrutura Blazor.- O nome da variável
dotNetHelper
é arbitrário e pode ser alterado para qualquer nome preferencial. - O componente deve descartar explicitamente o DotNetObjectReference para permitir a coleta de lixo e evitar uma perda de memória.
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);
}
No exemplo anterior, o nome da variável dotNetHelper
é arbitrário e pode ser alterado para qualquer nome preferencial.
@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();
}
}
No exemplo anterior:
JS
é uma instância injetada IJSRuntime. IJSRuntime é registrado pela estrutura Blazor.- O nome da variável
dotNetHelper
é arbitrário e pode ser alterado para qualquer nome preferencial. - O componente deve descartar explicitamente o DotNetObjectReference para permitir a coleta de lixo e evitar uma perda de memória.
<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>
No exemplo anterior:
- A classe
GreetingHelpers
é adicionada ao objetowindow
para definir globalmente a classe, o que permite que Blazor localize a classe para interoperabilidade JS. - O nome da variável
dotNetHelper
é arbitrário e pode ser alterado para qualquer nome preferencial.
Observação
Para obter diretrizes gerais sobre o local JS e nossas recomendações para aplicativos de produção, consulte Localização do JavaScript em aplicativos ASP.NET Core Blazor.
Chamar os métodos de classe genérica do .NET
As funções JavaScript (JS) podem chamar os métodos de classe genérica do .NET, onde uma função JS chama um método do .NET de uma classe genérica.
Na seguinte classe de tipo genérico (GenericType<TValue>
):
- A classe tem um único parâmetro de tipo (
TValue
) com uma única propriedade genéricaValue
. - A classe tem dois métodos não genéricos marcados com o atributo
[JSInvokable]
, cada um com um parâmetro de tipo genérico chamadonewValue
:Update
atualiza de forma síncrona o valor deValue
a partir denewValue
.UpdateAsync
atualiza de forma assíncrona o valor deValue
a partir denewValue
depois de criar uma tarefa aguardável com Task.Yield, que retorna de forma assíncrona para o contexto atual, quando aguardada.
- Cada um dos métodos de classe grava o tipo de
TValue
e o valor deValue
no console. Gravar no console é apenas para fins de demonstração. Os aplicativos de produção geralmente evitam gravar no console em favor do log do aplicativo. Para obter mais informações, confira Registro em log Blazor do ASP.NET Core e Registro em log no .NET Core e ASP.NET Core.
Observação
Tipos e métodos genéricos abertos não especificam tipos para espaços reservados de tipo. Por outro lado, genéricos fechados fornecem tipos para todos os espaços reservados de tipo. Os exemplos nesta seção demonstram genéricos fechados, mas a invocação de JSmétodos de instância de interoperabilidade de com genéricos abertos é compatível. Não é possível usar genéricos abertos para as invocações do método estático do .NET, que foram descritas anteriormente neste artigo.
Para obter mais informações, consulte os seguintes artigos:
- Classes e métodos genéricos (documentação do C#)
- Classes genéricas (Guia de Programação em C#)
- Genéricos no .NET (documentação do .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}");
}
}
Na função invokeMethodsAsync
a seguir:
- Os métodos
Update
eUpdateAsync
da classe de tipo genérico são chamados com argumentos que representam cadeias de caracteres e números. - Os componentes do lado do cliente dão suporte aos métodos de chamada do .NET de forma síncrona com
invokeMethod
.syncInterop
recebe um valor booliano que indica se a interoperabilidade JS está ocorrendo no cliente. QuandosyncInterop
étrue
,invokeMethod
é chamado com segurança. Se o valor desyncInterop
efalse
, somente a função assíncronainvokeMethodAsync
será chamada porque a interoperabilidade JS está em execução em um componente do lado do servidor. - Para fins de demonstração, a chamada de função DotNetObjectReference (
invokeMethod
ouinvokeMethodAsync
), o método do .NET chamado (Update
ouUpdateAsync
) e o argumento são gravados no console. Os argumentos usam um número aleatório para permitir a correspondência da chamada de função JS com a invocação do método do .NET (também gravada no console no lado do .NET). O código de produção geralmente não grava no console, seja no cliente ou no servidor. Os aplicativos de produção geralmente dependem do log do aplicativo. Para obter mais informações, confira Registro em log Blazor do ASP.NET Core e Registro em log no .NET Core e 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>
Observação
Para obter diretrizes gerais sobre o local JS e nossas recomendações para aplicativos de produção, consulte Localização do JavaScript em aplicativos ASP.NET Core Blazor.
No seguinte componente GenericsExample
:
- A JS da função
invokeMethodsAsync
é chamada quando o botãoInvoke Interop
é selecionado. - Um par de tipos DotNetObjectReference é criado e passado para a função JS para instâncias do
GenericType
como umstring
e umint
.
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();
}
}
No exemplo anterior, JS
é uma instância injetada IJSRuntime. IJSRuntime é registrado pela estrutura Blazor.
O seguinte demonstra a saída típica do exemplo anterior quando o botão Invoke Interop
é selecionado em um componente do lado do cliente:
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
Se o exemplo anterior for implementado em um componente do lado do servidor, as chamadas síncronas com invokeMethod
serão evitadas. Para componentes do lado do servidor, recomendamos a função assíncrona (invokeMethodAsync
) em vez da versão síncrona (invokeMethod
).
Saída típica de um componente do lado do servidor:
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
Os exemplos de saída anteriores demonstram que métodos assíncronos são executados e concluídos em uma ordem arbitrária, dependendo de vários fatores, incluindo o agendamento de thread e a velocidade da execução do método. Não é possível prever de forma confiável a ordem de conclusão para chamadas de método assíncrono.
Exemplos de instância de classe
A seguinte função de sayHello1
JS:
- Chama o método do .NET
GetHelloMessage
no DotNetObjectReference passado. - Retorna a mensagem de
GetHelloMessage
para o chamadorsayHello1
.
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Observação
Para obter diretrizes gerais sobre o local JS e nossas recomendações para aplicativos de produção, consulte Localização do JavaScript em aplicativos ASP.NET Core Blazor.
No exemplo anterior, o nome da variável dotNetHelper
é arbitrário e pode ser alterado para qualquer nome preferencial.
A classe HelloHelper
a seguir tem um método do .NET que pode ser invocado por JS chamado GetHelloMessage
. Quando HelloHelper
é criado, o nome na Name
propriedade é usado para retornar uma mensagem de 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}!";
}
O método CallHelloHelperGetHelloMessage
na classe JsInteropClasses3
a seguir invoca o JS da função sayHello1
com uma nova instância de 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);
}
}
Para evitar um vazamento de memória e permitir a coleta de lixo, a referência de objeto do .NET criada por DotNetObjectReference é descartada, quando a referência de objeto sai do escopo com a sintaxe using var
.
Quando o botão Trigger .NET instance method
é selecionado no componente a seguir, JsInteropClasses3.CallHelloHelperGetHelloMessage
é chamado com o valor de 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);
}
}
A imagem a seguir mostra o componente renderizado com o nome Amy Pond
no campo Name
. Depois que o botão é selecionado, Hello, Amy Pond!
é exibido na interface do usuário:
O padrão anterior mostrado na classe JsInteropClasses3
também pode ser implementado inteiramente em um componente.
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);
}
}
Para evitar um vazamento de memória e permitir a coleta de lixo, a referência de objeto do .NET criada por DotNetObjectReference é descartada, quando a referência de objeto sai do escopo com a sintaxe using var
.
A saída exibida pelo componente é Hello, Amy Pond!
, quando o nome Amy Pond
é fornecido no campo name
.
No componente anterior, a referência de objeto do .NET é descartada. Se uma classe ou componente não descartar o DotNetObjectReference, descarte-o no cliente chamando dispose
no DotNetObjectReference passado:
window.{JS FUNCTION NAME} = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('{.NET METHOD ID}');
dotNetHelper.dispose();
}
No exemplo anterior:
- O espaço reservado
{JS FUNCTION NAME}
é o nome da função JS. - O nome da variável
dotNetHelper
é arbitrário e pode ser alterado para qualquer nome preferencial. - O espaço reservado
{.NET METHOD ID}
é o identificador do método do .NET.
Classe auxiliar do método de instância do .NET do componente
Uma classe auxiliar pode invocar um método de instância do .NET como um Action. As classes auxiliares são úteis em cenários em que o uso de métodos .NET estáticos não é aplicável:
- Quando vários componentes do mesmo tipo são renderizados na mesma página.
- Em aplicativos do lado do servidor com vários usuários simultaneamente usando o mesmo componente.
No exemplo a seguir:
- O componente contém vários
ListItem1
componentes. - Cada componente
ListItem1
consiste em uma mensagem e um botão. - Quando um botão de componente
ListItem1
é selecionado, esse métodoListItem1
doUpdateMessage
altera o texto do item de lista e oculta o botão.
A classe MessageUpdateInvokeHelper
a seguir mantém um método do .NET que pode invocado por JS, UpdateMessageCaller
, para invocar o Action especificado quando a classe é instanciada.
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();
}
}
A função updateMessageCaller
JS a seguir invoca o método do .NET UpdateMessageCaller
.
<script>
window.updateMessageCaller = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('UpdateMessageCaller');
dotNetHelper.dispose();
}
</script>
Observação
Para obter diretrizes gerais sobre o local JS e nossas recomendações para aplicativos de produção, consulte Localização do JavaScript em aplicativos ASP.NET Core Blazor.
No exemplo anterior, o nome da variável dotNetHelper
é arbitrário e pode ser alterado para qualquer nome preferencial.
O componente ListItem1
a seguir é um componente compartilhado que pode ser usado várias vezes em um componente pai e cria itens de lista (<li>...</li>
) para uma lista HTML (<ul>...</ul>
ou <ol>...</ol>
). Cada instância ListItem1
de componente estabelece uma instância de MessageUpdateInvokeHelper
com um Action definido como o método UpdateMessage
.
Quando o botão ListItem1
de um componente InteropCall
é selecionado, updateMessageCaller
é invocado com um DotNetObjectReference criado para a instância MessageUpdateInvokeHelper
. Isso permite que a estrutura chame UpdateMessageCaller
na instância ListItem1
desse MessageUpdateInvokeHelper
. O DotNetObjectReference passado é descartado em 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
é chamado para atualizar a interface do usuário quando message
é definido em UpdateMessage
. Se StateHasChanged
não for chamado, Blazor não terá como saber que a interface do usuário deve ser atualizada quando o Action for invocado.
O componente pai a seguir inclui quatro itens de lista, cada um é uma instância do componente 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>
A imagem a seguir mostra o componente pai renderizado após a seleção do segundo botão InteropCall
:
- O segundo componente
ListItem1
exibiu a mensagemUpdateMessage Called!
. - O botão
InteropCall
para o segundo componenteListItem1
não está visível porque a propriedadedisplay
do CSS do botão está definida comonone
.
Método de instância do .NET do componente chamado a partir do DotNetObjectReference
atribuído a uma propriedade de elemento
A atribuição de um DotNetObjectReference a uma propriedade de um elemento HTML permite os métodos de chamada do .NET em uma instância de componente:
- Uma referência de elemento é capturada (ElementReference).
- No método
OnAfterRender{Async}
do componente, uma função JavaScript (JS) é invocada com a referência do elemento e a instância do componente como um DotNetObjectReference. A função JS anexa o DotNetObjectReference ao elemento em uma propriedade. - Quando um evento de elemento é invocado em JS (por exemplo,
onclick
), o DotNetObjectReference anexado do elemento é usado para chamar um método do .NET.
Semelhante à abordagem descrita na seção Classe auxiliar do método .NET da instância do componente, essa abordagem é útil em cenários em que o uso de métodos .NET estáticos não é aplicável:
- Quando vários componentes do mesmo tipo são renderizados na mesma página.
- Em aplicativos do lado do servidor com vários usuários simultaneamente usando o mesmo componente.
- O método do .NET é invocado a partir de um evento JS (por exemplo,
onclick
), não de um evento Blazor (por exemplo,@onclick
).
No exemplo a seguir:
- O componente contém vários
ListItem2
componentes , que é componente compartilhado. - Cada componente
ListItem2
consiste em uma mensagem de item de lista<span>
e uma segunda<span>
com uma propriedade do CSSdisplay
definida comoinline-block
para exibição. - Quando um item de lista de componentes
ListItem2
é selecionado, esse métodoListItem2
doUpdateMessage
altera o texto do item de lista no primeiro<span>
e oculta o segundo<span>
, definindo adisplay
propriedade comonone
.
A função assignDotNetHelper
JS a seguir atribui o DotNetObjectReference a um elemento em uma propriedade chamada dotNetHelper
. A função interopCall
JS a seguir usa o DotNetObjectReference para que o elemento passado invoque um método do .NET chamado 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>
Observação
Para obter diretrizes gerais sobre o local JS e nossas recomendações para aplicativos de produção, consulte Localização do JavaScript em aplicativos ASP.NET Core Blazor.
No exemplo anterior, o nome da variável dotNetHelper
é arbitrário e pode ser alterado para qualquer nome preferencial.
O componente ListItem2
a seguir é um componente compartilhado que pode ser usado várias vezes em um componente pai e cria itens de lista (<li>...</li>
) para uma lista HTML (<ul>...</ul>
ou <ol>...</ol>
).
Cada instância de componente ListItem2
invoca a função assignDotNetHelper
JS em OnAfterRenderAsync
com uma referência de elemento (o primeiro elemento <span>
do item de lista) e a instância do componente como um DotNetObjectReference.
Quando a mensagem ListItem2
de um componente <span>
é selecionada, interopCall
é invocado passando o elemento <span>
como um parâmetro (this
), que invoca o método do .NET UpdateMessage
. No UpdateMessage
, StateHasChanged
é chamado para atualizar a interface do usuário quando message
é definido e a propriedade display
do segundo <span>
é atualizada. Se StateHasChanged
não for chamado, Blazor não terá como saber que a interface do usuário deve ser atualizada quando o método for invocado.
O DotNetObjectReference é descartado quando o componente é descartado.
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)
{
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 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)
{
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();
}
O componente pai a seguir inclui quatro itens de lista, cada um é uma instância do componente 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>
Interoperabilidade JS síncrona em componentes do lado do cliente
Esta seção se aplica somente a componentes do lado do cliente.
As chamadas de interoperabilidade de JS são assíncronas, independentemente do código chamado ser síncrono ou assíncrono. As chamadas são assíncronas para garantir que os componentes sejam compatíveis nos modos de renderização do lado do servidor e do lado cliente. No servidor, todas as chamadas de interoperabilidade de JS devem ser assíncronas porque são enviadas por uma conexão de rede.
Se tiver certeza de que seu componente só é executado no WebAssembly, você pode optar por fazer chamadas de interoperabilidade de JS síncronas. Isso tem um pouco menos sobrecarga do que fazer chamadas assíncronas e pode resultar em menos ciclos de renderização, pois não há estado intermediário enquanto aguarda resultados.
Para fazer uma chamada síncrona do JavaScript para o .NET em um componente do lado do cliente, use DotNet.invokeMethod
em vez de DotNet.invokeMethodAsync
.
As chamadas síncronas funcionarão se:
Localização do JavaScript
Carregue o código JavaScript (JS) usando qualquer uma das abordagens descritas no artigo sobre a localização do JavaScript:
- Carregar um script na marcação de
<head>
(geralmente, não recomendado) - Carregar um script na marcação de
<body>
- Carregar um script de um arquivo JavaScript externo (
.js
) agrupado com um componente - Carregar um script de um arquivo JavaScript externo (
.js
) - Injetar um script antes ou após o início de Blazor
O uso de módulos JS para carregar JS é descrito neste artigo na seção Isolamento de JavaScript em módulos JavaScript.
Aviso
Coloque apenas uma tag <script>
em um arquivo de componente (.razor
), caso o componente tenha a garantia de adotar a SSR estática (renderização estática do lado do servidor) porque a tag <script>
não pode ser atualizada de forma dinâmica.
Aviso
Não coloque uma marca <script>
em um arquivo de componente (.razor
) porque a marca <script>
não pode ser atualizada dinamicamente.
Isolamento de JavaScript em módulos JavaScript
Blazor habilita o isolamento de JavaScript (JS) em módulos JavaScript padrão (especificação ECMAScript). O carregamento do módulo JavaScript funciona da mesma maneira em Blazor que para outros tipos de aplicativos Web, e você é livre para personalizar como os módulos são definidos em seu aplicativo. Para obter um guia sobre como usar módulos JavaScript, confira Docs da Web do MDN: módulos JavaScript.
O isolamento de JS oferece os seguintes benefícios:
- O JS importado não polui mais o namespace global.
- Os consumidores de uma biblioteca e componentes não precisam importar o JS relacionado.
Para obter mais informações, consulte Chamar funções JavaScript de métodos .NET no ASP.NET Core Blazor.
Há suporte para importação dinâmica com o import()
operador com ASP.NET Core e Blazor:
if ({CONDITION}) import("/additionalModule.js");
No exemplo anterior, o espaço reservado {CONDITION}
representa uma verificação condicional para determinar se o módulo deve ser carregado.
Para obter compatibilidade com o navegador, confira Posso usar: módulos JavaScript: importação dinâmica.
Evitar referências de objeto circular
Os objetos que contêm referências circulares não podem ser serializados no cliente para:
- Chamadas de método do .NET.
- O método do JavaScript chama a partir do C# quando o tipo de retorno tem referências circulares.
Suporte à matriz de bytes
Blazor dá suporte à interoperabilidade do JavaScript (JS) da matriz de bytes otimizada que evita codificar/decodificar matrizes de bytes em Base64. O exemplo a seguir usa a interoperabilidade do JS para passar uma matriz de bytes para o .NET.
Forneça uma função sendByteArray
JS. A função é chamada estaticamente, o que inclui o parâmetro de nome de assembly na chamada invokeMethodAsync
, usando um botão no componente, e não retorna um valor:
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>
Observação
Para obter diretrizes gerais sobre o local JS e nossas recomendações para aplicativos de produção, consulte Localização do JavaScript em aplicativos ASP.NET Core Blazor.
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));
}
}
Para obter informações sobre como usar uma matriz de bytes ao chamar JavaScript a partir do .NET, confira Chamar funções JavaScript a partir dos métodos do .NET no Blazor do ASP.NET Core.
Transmitir do JavaScript para o .NET
Blazor dá suporte a dados de streaming diretamente do JavaScript para o .NET. Os fluxos são solicitados usando a interface Microsoft.JSInterop.IJSStreamReference
.
Microsoft.JSInterop.IJSStreamReference.OpenReadStreamAsync
retorna um Stream e usa os seguintes parâmetros:
maxAllowedSize
: número máximo de bytes permitidos para a operação de leitura do JavaScript, que usa o padrão 512.000 bytes, se não especificado.cancellationToken
: um CancellationToken para cancelar a leitura.
No JavaScript:
function streamToDotNet() {
return new Uint8Array(10000000);
}
No código 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);
No exemplo anterior:
JS
é uma instância injetada IJSRuntime. IJSRuntime é registrado pela estrutura Blazor.- O
dataReferenceStream
é gravado em disco (file.txt
) no caminho da pasta temporária do usuário atual (GetTempPath).
Chamar as funções do JavaScript a partir de métodos do .NET no Blazor do ASP.NET Core abrange a operação inversa, transmitindo do .NET para o JavaScript usando um DotNetStreamReference.
Os uploads de arquivos Blazor do ASP.NET Core abrangem como carregar um arquivo no Blazor. Para obter um exemplo de formulários que transmite dados <textarea>
em um componente do lado do servidor, consulte Solucionar problemas de formulários do ASP.NET Core Blazor.
Interoperabilidade [JSImport]
/[JSExport]
JavaScript
Esta seção se aplica a componentes do lado do cliente.
Como alternativa à interação com o JavaScript (JS) em componentes do lado do cliente que usam o mecanismo de interoperabilidade JS de Blazor com base na interface IJSRuntime, uma API de interoperabilidade JS[JSImport]
/[JSExport]
está disponível para aplicativos direcionados ao .NET 7 ou posterior.
Para obter mais informações, consulte Interop de JSImport/JSExport de JavaScript com o ASP.NET Core Blazor.
Descarte de referências de objeto de interoperabilidade do JavaScript
Os exemplos em todos os artigos de interoperabilidade do JavaScript (JS) demonstram padrões típicos de descarte de objetos:
Ao chamar o .NET a partir de JS, conforme descrito neste artigo, descarte um DotNetObjectReference criado a partir do .NET ou do JS para evitar o vazamento de memória do .NET.
Ao chamar JS a partir do .NET, conforme descrito em Chamar funções JavaScript a partir de métodos do .NET no Blazor do ASP.NET Core, descarte qualquer IJSObjectReference/IJSInProcessObjectReference/
JSObjectReference
criado a partir do .NET ou do JS para evitar perda de memória JS.
As referências de objeto de interoperabilidade JS são implementadas como um mapa com chave de um identificador no lado da chamada de interoperabilidade JS que cria a referência. Quando o descarte de objetos é iniciado no .NET ou do lado do JS, Blazor remove a entrada do mapa e o objeto pode ser coletado como lixo, desde que nenhuma outra referência forte ao objeto esteja presente.
No mínimo, sempre descarte objetos criados no lado do .NET para evitar o vazamento de memória gerenciada do .NET.
Tarefas de limpeza do DOM no descarte de componentes
Para obter mais informações, confira Interoperabilidade ASP.NET Core Blazor JavaScript (interoperabilidade JS).
Chamadas de interoperabilidade do JavaScript sem um circuito
Para obter mais informações, confira Interoperabilidade ASP.NET Core Blazor JavaScript (interoperabilidade JS).
Recursos adicionais
- Chamar funções JavaScript de métodos .NET no ASP.NET Core Blazor
- Exemplo
InteropComponent.razor
(branchmain
do repositório do GitHubdotnet/AspNetCore
): o branchmain
representa o desenvolvimento atual da unidade de produto para a próxima versão do ASP.NET Core. Para selecionar a ramificação para uma versão diferente (por exemplo,release/{VERSION}
, em que o espaço reservado{VERSION}
é a versão de lançamento), use a lista suspensa Alternar ramificações ou marcas para selecionar a ramificação. Para uma ramificação que não existe mais, use a guia Tags para localizar a API (por exemplo,v7.0.0
). - Interação com o DOM
- Blazor Repositório GitHub de exemplos(
dotnet/blazor-samples
) (como baixar) - Tratar erros em aplicativos Blazor do ASP.NET Core (seção Interoperabilidade do JavaScript)
- Mitigação de ameaças: métodos do .NET invocados no navegador