Llamada a métodos de .NET desde funciones de JavaScript en ASP.NET Core Blazor
Nota
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Advertencia
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulta la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulta la versión .NET 8 de este artículo.
Importante
Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.
Para la versión actual, consulte la versión de .NET 9 de este artículo.
En este artículo se explica cómo invocar métodos de .NET desde JavaScript (JS).
Para obtener información sobre cómo llamar a funciones de JS desde .NET, consulta Llamada a funciones de JavaScript con métodos de .NET en Blazor de ASP.NET Core.
Invocación de un método de .NET estático
Para invocar un método de .NET estático desde JavaScript (JS), usa las funciones de JS:
DotNet.invokeMethodAsync
(recomendado): asincrónico para los componentes del lado servidor y del lado cliente.DotNet.invokeMethod
: sincrónico solo para los componentes del lado cliente.
Pasa el nombre del ensamblado que contiene el método, el identificador del método estático de .NET y cualquier argumento.
En el ejemplo siguiente:
- El marcador de posición
{ASSEMBLY NAME}
es el nombre de ensamblado de la aplicación. - El marcador de posición
{.NET METHOD ID}
es el identificador del método de .NET. - El marcador de posición
{ARGUMENTS}
son argumentos opcionales separados por comas que se pasan al método, y cada uno de ellos debe ser serializable con JSON.
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});
DotNet.invokeMethodAsync
devuelve un JS Promise
que representa el resultado de la operación. DotNet.invokeMethod
(componentes del lado cliente) devuelve el resultado de la operación.
Importante
En el caso de los componentes del lado servidor, se recomienda la función asincrónica (invokeMethodAsync
) a través de la versión sincrónica (invokeMethod
).
El método de .NET debe ser público y estático, y debe tener el atributo [JSInvokable]
.
En el ejemplo siguiente:
- El marcador de posición
{<T>}
indica el tipo de valor devuelto, que solo es necesario para los métodos que devuelven un valor. - El marcador de posición
{.NET METHOD ID}
es el identificador del método.
@code {
[JSInvokable]
public static Task{<T>} {.NET METHOD ID}()
{
...
}
}
Nota
La llamada a métodos genéricos abiertos no se admite con métodos estáticos de .NET, pero se admite con métodos de instancia. Para obtener más información, consulta la sección Llamada a métodos de clase genérica de .NET.
En el siguiente componente, el método de C# ReturnArrayAsync
devuelve una matriz int
. El atributo [JSInvokable]
se aplica al método, lo que hace que el método sea invocable 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)
{
await module.DisposeAsync();
}
}
}
CallDotnet1.razor.js
:
export function returnArrayAsync() {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
.then(data => {
console.log(data);
});
}
export function addHandlers() {
const btn = document.getElementById("btn");
btn.addEventListener("click", returnArrayAsync);
}
La función addHandlers
JS agrega un evento click
al botón. La función returnArrayAsync
JS se asigna como controlador.
La función returnArrayAsync
JS llama al método ReturnArrayAsync
.NET del componente, que registra el resultado en la consola de herramientas para desarrolladores web del explorador. BlazorSample
es el nombre del ensamblado de la aplicación.
CallDotnet1.razor
:
@page "/call-dotnet-1"
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 1</PageTitle>
<h1>Call .NET Example 1</h1>
<p>
<button id="btn">Trigger .NET static method</button>
</p>
<p>
See the result in the developer tools console.
</p>
@code {
private IJSObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/Pages/CallDotnet1.razor.js");
await module.InvokeVoidAsync("addHandlers");
}
}
[JSInvokable]
public static Task<int[]> ReturnArrayAsync() =>
Task.FromResult(new int[] { 11, 12, 13 });
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
CallDotnet1.razor.js
:
export function returnArrayAsync() {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
.then(data => {
console.log(data);
});
}
export function addHandlers() {
const btn = document.getElementById("btn");
btn.addEventListener("click", returnArrayAsync);
}
La función addHandlers
JS agrega un evento click
al botón. La función returnArrayAsync
JS se asigna como controlador.
La función returnArrayAsync
JS llama al método ReturnArrayAsync
.NET del componente, que registra el resultado en la consola de herramientas para desarrolladores web del explorador. BlazorSample
es el nombre del ensamblado de la aplicación.
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 });
}
}
El atributo HTML onclick
del elemento <button>
es la asignación del controlador de eventos onclick
de JavaScript para procesar eventos click
, y no el atributo de directiva @onclick
de Blazor. La función returnArrayAsync
JS se asigna como controlador.
La siguiente función returnArrayAsync
JS, llama al método ReturnArrayAsync
.NET del componente, que registra el resultado en la consola de herramientas para desarrolladores web del explorador. BlazorSample
es el nombre del ensamblado de la aplicación.
<script>
window.returnArrayAsync = () => {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
.then(data => {
console.log(data);
});
};
</script>
Nota:
Para obtener una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulta Ubicación de JavaScript en aplicaciones Blazor de ASP.NET Core.
Cuando se selecciona el botón Trigger .NET static method
, la salida de la consola de las herramientas de desarrollo del explorador muestra los datos de la matriz. El formato de la salida difiere ligeramente entre los exploradores. En la salida siguiente se muestra el formato utilizado por Microsoft Edge:
Array(3) [ 11, 12, 13 ]
Para pasar datos a un método .NET al llamar a la función invokeMethodAsync
, páselos como argumentos.
Para mostrar el paso de datos a .NET, pasa una posición inicial al método ReturnArrayAsync
donde se invoca al método en 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>
El método invocable ReturnArrayAsync
del componente recibe la posición inicial y construye la matriz a partir de él. La matriz se devuelve para el registro en la consola:
[JSInvokable]
public static Task<int[]> ReturnArrayAsync(int startPosition) =>
Task.FromResult(Enumerable.Range(startPosition, 3).ToArray());
Una vez que se vuelve a compilar la aplicación y se actualiza el explorador, la salida siguiente aparece en la consola del explorador cuando se selecciona el botón:
Array(3) [ 14, 15, 16 ]
El identificador de método de .NET para la llamada de JS es el nombre del método de .NET, pero puedes especificar un identificador distinto mediante el constructor del atributo [JSInvokable]
. En el ejemplo siguiente, DifferentMethodName
es el identificador del método asignado para el método ReturnArrayAsync
:
[JSInvokable("DifferentMethodName")]
En la llamada a DotNet.invokeMethodAsync
(componentes del lado servidor o del lado cliente) o DotNet.invokeMethod
(solo componentes del lado cliente), llama DifferentMethodName
a para ejecutar el ReturnArrayAsync
método .NET:
DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
DotNet.invokeMethod('BlazorSample', 'DifferentMethodName');
(sólo componentes del lado del cliente)
Nota:
El ejemplo de método ReturnArrayAsync
de esta sección devuelve el resultado de un Task sin el uso de las palabras clave async
y await
de C# explícitas. La codificación de métodos con async
y await
es típica de los métodos que usan la palabra clave await
para devolver el valor de las operaciones asincrónicas.
El método ReturnArrayAsync
compuesto con las palabras clave async
y await
:
[JSInvokable]
public static async Task<int[]> ReturnArrayAsync() =>
await Task.FromResult(new int[] { 11, 12, 13 });
Para obtener más información, consulta Programación asincrónica con async y await en la guía de C#.
Creación de referencias de datos y objetos de JavaScript para pasar a .NET
Llama a DotNet.createJSObjectReference(jsObject)
para construir una referencia de objeto de JS para que se pueda pasar a .NET, donde jsObject
es el JS Object
utilizado para crear la referencia de objeto de JS. En el ejemplo siguiente se pasa una referencia al objeto window
no serializable a .NET, que lo recibe en el método de C# ReceiveWindowObject
como IJSObjectReference:
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', 'ReceiveWindowObject',
DotNet.createJSObjectReference(window));
[JSInvokable]
public static void ReceiveWindowObject(IJSObjectReference objRef)
{
...
}
En el ejemplo anterior, el marcador de posición {ASSEMBLY NAME}
es el nombre de espacio de la aplicación.
Nota
El ejemplo anterior no requiere la eliminación de JSObjectReference
, ya que una referencia al objeto window
no se mantiene en JS.
Mantener una referencia a JSObjectReference
requiere eliminarla para evitar la fuga de memoria de JS en el cliente. En el ejemplo siguiente se refactoriza el código anterior para capturar una referencia a JSObjectReference
, seguida de una llamada a DotNet.disposeJSObjectReference()
para eliminar la referencia:
var jsObjectReference = DotNet.createJSObjectReference(window);
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', 'ReceiveWindowObject', jsObjectReference);
DotNet.disposeJSObjectReference(jsObjectReference);
En el ejemplo anterior, el marcador de posición {ASSEMBLY NAME}
es el nombre de espacio de la aplicación.
Llama a DotNet.createJSStreamReference(streamReference)
para construir una referencia de flujo de JS de modo que se pueda pasar a .NET, donde streamReference
es ArrayBuffer
, Blob
o cualquier matriz con tipo, como Uint8Array
o Float32Array
, que se usa para crear la referencia de flujo de JS.
Invocación de un método de .NET de instancia
Para invocar un método de .NET de instancia desde JavaScript (JS):
Pasa la instancia de .NET por referencia a JS encapsulando la instancia en un DotNetObjectReference y llamando a Create en ella.
Invoca un método de instancia de .NET desde JS el uso
invokeMethodAsync
(recomendado) oinvokeMethod
(solo componentes del lado cliente) desde el objeto pasado DotNetObjectReference. Pasa el identificador del método .NET de instancia y todos los argumentos. La instancia de .NET también se puede pasar como argumento al invocar otros métodos de .NET desde JS.En el ejemplo siguiente:
dotNetHelper
es un objeto DotNetObjectReference.- El marcador de posición
{.NET METHOD ID}
es el identificador del método de .NET. - El marcador de posición
{ARGUMENTS}
son argumentos opcionales separados por comas que se pasan al método, y cada uno de ellos debe ser serializable con JSON.
dotNetHelper.invokeMethodAsync('{.NET METHOD ID}', {ARGUMENTS});
Nota:
invokeMethodAsync
yinvokeMethod
no aceptan un parámetro de nombre de ensamblado al invocar un método de instancia.invokeMethodAsync
devuelve un JSPromise
que representa el resultado de la operación.invokeMethod
(solo componentes del lado cliente) devuelve el resultado de la operación.Importante
En el caso de los componentes del lado servidor, se recomienda la función asincrónica (
invokeMethodAsync
) a través de la versión sincrónica (invokeMethod
).Deseche DotNetObjectReference.
En las siguientes secciones de este artículo muestran varios enfoques para invocar un método de .NET de instancia:
Prevención del recorte de los métodos de .NET invocables de JavaScript
Esta sección se aplica a las aplicaciones del lado del cliente con la compilación anticipada (AOT) y la revinculación en tiempo de ejecución activadas.
Varios de los ejemplos de las secciones siguientes se basan en un enfoque de instancia de clase, donde el método .NET invocable de JavaScript marcado con el atributo [JSInvokable]
es un miembro de una clase que no es un componente Razor. Cuando estos métodos de .NET se encuentran en un componente Razor, están protegidos contra la revinculación en tiempo de ejecución o el recorte. Para proteger los métodos de .NET del recorte fuera de los componentes Razor, implementa los métodos con el atributo DynamicDependency
en el constructor de la clase, tal como se muestra en el ejemplo siguiente:
using System.Diagnostics.CodeAnalysis;
using Microsoft.JSInterop;
public class ExampleClass {
[DynamicDependency(nameof(ExampleJSInvokableMethod))]
public ExampleClass()
{
}
[JSInvokable]
public string ExampleJSInvokableMethod()
{
...
}
}
Para obtener más información, consulta Preparación de las bibliotecas de .NET para el recorte: DynamicDependency.
Pasar DotNetObjectReference
a una función de JavaScript individual
En el ejemplo de esta sección enseña cómo pasar DotNetObjectReference a una función JavaScript (JS) individual.
La función sayHello1
de JS siguiente recibe DotNetObjectReference y llama a invokeMethodAsync
para invocar el método de .NET GetHelloMessage
de un componente:
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Nota:
Para obtener una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulta Ubicación de JavaScript en aplicaciones Blazor de ASP.NET Core.
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Para el componente siguiente:
- El componente tiene un método de .NET invocable por JS llamado
GetHelloMessage
. - Cuando se selecciona el botón
Trigger .NET instance method
, se llama a la funciónsayHello1
de JS con DotNetObjectReference. sayHello1
:- Llama a
GetHelloMessage
y recibe el resultado del mensaje. - Devuelve el resultado del mensaje al método
TriggerDotNetInstanceMethod
que realiza la llamada.
- Llama a
- El mensaje devuelto de
sayHello1
enresult
se muestra al usuario. - Para evitar una pérdida de memoria y permitir la recolección de elementos no utilizados, la referencia de objeto de .NET creada por DotNetObjectReference se elimina en el 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();
}
}
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Usa las instrucciones siguientes para pasar argumentos a un método de instancia:
Agrega parámetros a la invocación del método de .NET. En el ejemplo siguiente, se pasa un nombre al método. Agrega parámetros adicionales a la lista según sea necesario.
<script>
window.sayHello2 = (dotNetHelper, name) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage', name);
};
</script>
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Proporciona la lista de parámetros al método de .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();
}
}
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Pasar DotNetObjectReference
a una clase con varias funciones de JavaScript
En el ejemplo de esta sección enseña cómo pasar DotNetObjectReference a una clase JavaScript (JS) con varias funciones.
Crea y pasa DotNetObjectReference desde el método de ciclo de vida OnAfterRenderAsync
a una clase JS para usar varias funciones. Asegúrate de que el código .NET elimina DotNetObjectReference, como se muestra en el ejemplo siguiente.
En el siguiente componente, los botones Trigger JS function
llaman a funciones de JS, para lo que establecen la propiedad onclick
de JS, no el atributo de la directiva @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)
{
await module.DisposeAsync();
}
dotNetHelper?.Dispose();
}
}
En el ejemplo anterior:
JS
es una instancia de IJSRuntime insertada. El marco Blazor registra IJSRuntime.- En el ejemplo anterior, el nombre de la variable
dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido. - El componente debe eliminar explícitamente DotNetObjectReference para permitir la recolección de elementos no utilizados y evitar una pérdida de memoria.
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);
}
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
@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();
}
}
En el ejemplo anterior:
JS
es una instancia de IJSRuntime insertada. El marco Blazor registra IJSRuntime.- En el ejemplo anterior, el nombre de la variable
dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido. - El componente debe eliminar explícitamente DotNetObjectReference para permitir la recolección de elementos no utilizados y evitar una pérdida de memoria.
<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>
En el ejemplo anterior:
- La clase
GreetingHelpers
se agrega al objetowindow
para definir globalmente la clase, lo que permite a Blazor buscar la clase para la interoperabilidad de JS. - En el ejemplo anterior, el nombre de la variable
dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Nota:
Para obtener una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulta Ubicación de JavaScript en aplicaciones Blazor de ASP.NET Core.
Llamada a métodos de clase genérica de .NET
Las funciones JavaScript (JS) pueden llamar a métodos de clase genérica de .NET, donde una función JS llama a un método .NET de una clase genérica.
En la clase de tipo genérico siguiente (GenericType<TValue>
):
- La clase tiene un único parámetro de tipo (
TValue
) con una sola propiedadValue
genérica. - La clase tiene dos métodos no genéricos marcados con el atributo
[JSInvokable]
, cada uno con un parámetro de tipo genérico denominadonewValue
:Update
actualiza sincrónicamente el valor deValue
desdenewValue
.UpdateAsync
actualiza de asincrónicamente el valor deValue
desdenewValue
después de crear una tarea por la que se puede esperar con Task.Yield, que vuelve a suspenderse asincrónicamente al contexto actual cuando se espera por dicho elemento.
- Cada uno de los métodos de clase escribe el tipo de
TValue
y el valor deValue
en la consola. La escritura en la consola solo tiene fines de demostración. Las aplicaciones de producción normalmente evitan escribir en la consola en favor del registro de la aplicación. Para obtener más información, consulta Registro de Blazor en ASP.NET Core y Registro en .NET Core y ASP.NET Core.
Nota
Los tipos y métodos genéricos abiertos no especifican tipos para los marcadores de posición de tipo. Por el contrario, los genéricos cerrados suministran tipos para todos los marcadores de posición de tipo. Los ejemplos de esta sección muestran genéricos cerrados, pero la invocación de interoperabilidad de JS de métodos de instancia con genéricos abiertos se admite. El uso de genéricos abiertos no se admite para las invocaciones de métodos estáticos de .NET, que se describieron anteriormente en este artículo.
Consulta los siguientes artículos para más información:
- Clases y métodos genéricos (documentación de C#)
- Clases genéricas (Guía de programación de C#)
- Genéricos en .NET (documentación de .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}");
}
}
En la siguiente función invokeMethodsAsync
:
- Los métodos
Update
yUpdateAsync
de la clase de tipo genérico se llaman con argumentos que representan cadenas y números. - Los componentes del lado cliente admiten la llamada a métodos de .NET de forma sincrónica con
invokeMethod
.syncInterop
recibe un valor booleano que indica si la interoperabilidad JS se está produciendo en el cliente. CuandosyncInterop
estrue
, se llama ainvokeMethod
de forma segura. Si el valor desyncInterop
esfalse
, solo se llama a la función asíncronainvokeMethodAsync
porque la interoperabilidad JS se está ejecutando en un componente del lado del servidor. - Con fines de demostración, la llamada de función DotNetObjectReference (
invokeMethod
oinvokeMethodAsync
), el método de .NET denominado (Update
oUpdateAsync
) y el argumento se escriben en la consola. Los argumentos usan un número aleatorio para permitir que la función JS llame a la invocación del método .NET (también escrita en la consola en el lado de .NET). Normalmente, el código de producción no escribe en la consola, ni en el cliente ni en el servidor. Las aplicaciones de producción normalmente se basan en el registro de la aplicación. Para obtener más información, consulta Registro de Blazor en ASP.NET Core y Registro en .NET Core y 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>
Nota:
Para obtener una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulta Ubicación de JavaScript en aplicaciones Blazor de ASP.NET Core.
En el componente GenericsExample
siguiente:
- Se llama a la función JS
invokeMethodsAsync
cuando se selecciona el botónInvoke Interop
. - Se crea un par de tipos DotNetObjectReference y se pasan a la función JS para las instancias de
GenericType
comostring
yint
.
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();
}
}
En el ejemplo anterior, JS
es una instancia de IJSRuntime insertada. El marco Blazor registra IJSRuntime.
A continuación se muestra la salida típica del ejemplo anterior cuando se selecciona el botón Invoke Interop
en un componente del lado del 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
Si el ejemplo anterior se implementa en un componente del lado del servidor, se evitan las llamadas síncronas con invokeMethod
. En el caso de los componentes del lado servidor, se recomienda la función asincrónica (invokeMethodAsync
) a través de la versión sincrónica (invokeMethod
).
Salida típica de un componente del lado 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
Los ejemplos de salida anteriores muestran que los métodos asincrónicos se ejecutan y completan en un orden arbitrario en función de varios factores, incluida la programación de subprocesos y la velocidad de ejecución del método. No es posible predecir de forma confiable el orden de finalización de las llamadas a métodos asincrónicos.
Ejemplos de instancias de clase
La siguiente función sayHello1
JS:
- Llama al método de .NET
GetHelloMessage
en la clase DotNetObjectReference pasada. - Devuelve el mensaje de
GetHelloMessage
al autor de la llamadasayHello1
.
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Nota:
Para obtener una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulta Ubicación de JavaScript en aplicaciones Blazor de ASP.NET Core.
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
La clase HelloHelper
siguiente tiene un método de .NET invocable por JS llamado GetHelloMessage
. Cuando se crea HelloHelper
, el nombre de la propiedad Name
se usa para devolver un mensaje 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}!";
}
El método CallHelloHelperGetHelloMessage
de la clase JsInteropClasses3
siguiente invoca la función sayHello1
de JS con una nueva instancia 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 una pérdida de memoria y permitir la recolección de elementos no utilizados, la referencia de objeto de .NET creada por DotNetObjectReference se elimina cuando la referencia de objeto sale del ámbito con la sintaxis using var
.
Cuando se selecciona el botón Trigger .NET instance method
en el siguiente componente, se llama a JsInteropClasses3.CallHelloHelperGetHelloMessage
con el 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);
}
}
En la imagen siguiente se muestra el componente representado con el nombre Amy Pond
en el campo Name
. Una vez seleccionado el botón, Hello, Amy Pond!
se muestra en la interfaz de usuario:
El patrón anterior que se muestra en la clase JsInteropClasses3
también se puede implementar íntegramente en un 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 una pérdida de memoria y permitir la recolección de elementos no utilizados, la referencia de objeto de .NET creada por DotNetObjectReference se elimina cuando la referencia de objeto sale del ámbito con la sintaxis using var
.
La salida que muestra el componente es Hello, Amy Pond!
cuando se especifica el nombre Amy Pond
en el campo name
.
En el componente anterior, se elimina la referencia al objeto de .NET. Si una clase o un componente no elimina DotNetObjectReference, deséchalo del cliente llamando a dispose
en la clase DotNetObjectReference pasada:
window.{JS FUNCTION NAME} = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('{.NET METHOD ID}');
dotNetHelper.dispose();
}
En el ejemplo anterior:
- El marcador de posición
{JS FUNCTION NAME}
es el nombre de la función JS. - En el ejemplo anterior, el nombre de la variable
dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido. - El marcador de posición
{.NET METHOD ID}
es el identificador del método de .NET.
Clase auxiliar del método de .NET de la instancia de componente
Una clase auxiliar puede invocar un método de instancia de .NET como Action. Las clases auxiliares son útiles en escenarios en los que no se aplican los métodos estáticos de .NET:
- Cuando se representan varios componentes del mismo tipo en la misma página.
- En aplicaciones del lado del servidor con varios usuarios utilizando simultáneamente el mismo componente.
En el ejemplo siguiente:
- El componente contiene varios
ListItem1
componentes. - Cada componente
ListItem1
consta de un mensaje y un botón. - Cuando se selecciona un botón de componente
ListItem1
, el métodoUpdateMessage
de ese objetoListItem1
cambia el texto del elemento de lista y oculta el botón.
La clase MessageUpdateInvokeHelper
siguiente mantiene un método de .NET invocable por JS, UpdateMessageCaller
, para invocar el elemento Action especificado cuando se crea una instancia de la clase.
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();
}
}
La siguiente función updateMessageCaller
JS invoca el método de .NET UpdateMessageCaller
.
<script>
window.updateMessageCaller = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('UpdateMessageCaller');
dotNetHelper.dispose();
}
</script>
Nota:
Para obtener una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulta Ubicación de JavaScript en aplicaciones Blazor de ASP.NET Core.
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
El componente ListItem1
siguiente es un componente compartido que se puede usar varias veces en un componente primario y crea elementos de lista (<li>...</li>
) para una lista HTML (<ul>...</ul>
o <ol>...</ol>
). Cada instancia de componente ListItem1
establece una instancia de MessageUpdateInvokeHelper
con un elemento Action establecido en su método UpdateMessage
.
Cuando se selecciona un botón InteropCall
del componente ListItem1
, se invoca a updateMessageCaller
con un elemento DotNetObjectReference creado para la instancia MessageUpdateInvokeHelper
. Esto permite que el marco llame a UpdateMessageCaller
en esa instancia MessageUpdateInvokeHelper
de ListItem1
. La clase DotNetObjectReference pasada se elimina en 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();
}
}
Se llama a StateHasChanged
para actualizar la interfaz de usuario cuando message
se establece en UpdateMessage
. Si no se llama a StateHasChanged
, Blazor no tiene ninguna manera de saber que la interfaz de usuario debe actualizarse cuando se invoca a Action.
El siguiente componente principal incluye cuatro elementos de lista, y cada uno de ellos es una instancia del 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>
En la imagen siguiente se muestra el componente principal representado después de que se selecciona el segundo botón InteropCall
:
- El segundo componente
ListItem1
ha mostrado el mensajeUpdateMessage Called!
. - El botón
InteropCall
del segundo componenteListItem1
no es visible porque la propiedaddisplay
de CSS del botón está establecida ennone
.
Método .NET de la instancia de componente al que se llama desde DotNetObjectReference
asignado a una propiedad de elemento
La asignación de un objeto DotNetObjectReference a una propiedad de un elemento HTML permite la llamada a métodos .NET en una instancia de componente:
- Se captura una referencia de elementos (ElementReference).
- En el método
OnAfterRender{Async}
del componente, se invoca una función de JavaScript (JS) con la referencia de elementos y la instancia de componente como DotNetObjectReference. La función JS adjunta DotNetObjectReference al elemento en una propiedad. - Cuando se invoca un evento de elemento en JS (por ejemplo,
onclick
), el objeto DotNetObjectReference adjunto del elemento se usa para llamar a un método .NET.
De forma similar al enfoque descrito en la sección Clase auxiliar del método .NET de instancia de componente, este enfoque es útil en escenarios en los que no se aplican los métodos estáticos de .NET:
- Cuando se representan varios componentes del mismo tipo en la misma página.
- En aplicaciones del lado del servidor con varios usuarios utilizando simultáneamente el mismo componente.
- El método .NET se invoca desde un evento JS (por ejemplo,
onclick
), no desde un evento Blazor (por ejemplo,@onclick
).
En el ejemplo siguiente:
- El componente contiene varios componentes
ListItem2
, que es un componente compartido. - Cada componente
ListItem2
consta de un mensaje de elemento de lista<span>
y de un segundo elemento<span>
con una propiedad CSSdisplay
establecida eninline-block
para la presentación. - Cuando se selecciona un elemento de lista de componentes
ListItem2
, el métodoListItem2
de eseUpdateMessage
cambia el texto del elemento de lista en la primera instancia de<span>
y oculta la segunda instancia de<span>
mediante el establecimiento de su propiedaddisplay
ennone
.
La siguiente función assignDotNetHelper
JS asigna DotNetObjectReference a un elemento de una propiedad denominada dotNetHelper
. La siguiente función interopCall
JS usa DotNetObjectReference del elemento pasado para invocar un método .NET denominado 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>
Nota:
Para obtener una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulta Ubicación de JavaScript en aplicaciones Blazor de ASP.NET Core.
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
El componente ListItem2
siguiente es un componente compartido que se puede usar varias veces en un componente primario y crea elementos de lista (<li>...</li>
) para una lista HTML (<ul>...</ul>
o <ol>...</ol>
).
Cada instancia del componente ListItem2
invoca la función JSassignDotNetHelper
en OnAfterRenderAsync
con una referencia de elementos (el primer elemento <span>
del elemento de lista) y la instancia del componente como DotNetObjectReference.
Cuando se selecciona el elemento <span>
de mensaje de un componente ListItem2
, se invoca interopCall
al pasar el elemento <span>
como parámetro (this
), que invoca el método .NET UpdateMessage
. En UpdateMessage
, se llama a StateHasChanged
para actualizar la interfaz de usuario cuando se establece message
y se actualiza la propiedad display
del segundo <span>
. Si no se llama a StateHasChanged
, Blazor no tiene ninguna forma de saber que la interfaz de usuario debe actualizarse cuando se invoca el método.
El elemento DotNetObjectReference se elimina al eliminar el componente.
ListItem2.razor
:
@inject IJSRuntime JS
@implements IAsyncDisposable
<li>
<span style="font-weight:bold;color:@color" @ref="elementRef"
@onclick="CallJSToInvokeDotnet">
@message
</span>
<span style="display:@display">
Not Updated Yet!
</span>
</li>
@code {
private IJSObjectReference? module;
private DotNetObjectReference<ListItem2>? objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
private string color = "initial";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/ListItem2.razor.js");
objRef = DotNetObjectReference.Create(this);
await module.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
public async void CallJSToInvokeDotnet()
{
if (module is not null)
{
await module.InvokeVoidAsync("interopCall", elementRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
color = "MediumSeaGreen";
StateHasChanged();
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
objRef?.Dispose();
}
}
@inject IJSRuntime JS
@implements IAsyncDisposable
<li>
<span style="font-weight:bold;color:@color" @ref="elementRef"
@onclick="CallJSToInvokeDotnet">
@message
</span>
<span style="display:@display">
Not Updated Yet!
</span>
</li>
@code {
private IJSObjectReference? module;
private DotNetObjectReference<ListItem2>? objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
private string color = "initial";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/ListItem2.razor.js");
objRef = DotNetObjectReference.Create(this);
await module.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
public async void CallJSToInvokeDotnet()
{
if (module is not null)
{
await module.InvokeVoidAsync("interopCall", elementRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
color = "MediumSeaGreen";
StateHasChanged();
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
objRef?.Dispose();
}
}
@inject IJSRuntime JS
<li>
<span @ref="elementRef" onclick="interopCall(this)">@message</span>
<span style="display:@display">Not Updated Yet!</span>
</li>
@code {
private DotNetObjectReference<ListItem2>? objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
objRef = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS
<li>
<span @ref="elementRef" onclick="interopCall(this)">@message</span>
<span style="display:@display">Not Updated Yet!</span>
</li>
@code {
private DotNetObjectReference<ListItem2>? objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
objRef = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS
<li>
<span @ref="elementRef" onclick="interopCall(this)">@message</span>
<span style="display:@display">Not Updated Yet!</span>
</li>
@code {
private DotNetObjectReference<ListItem2> objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
objRef = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS
<li>
<span @ref="elementRef" onclick="interopCall(this)">@message</span>
<span style="display:@display">Not Updated Yet!</span>
</li>
@code {
private DotNetObjectReference<ListItem2> objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
objRef = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
public void Dispose() => objRef?.Dispose();
}
El siguiente componente principal incluye cuatro elementos de lista, y cada uno de ellos es una instancia del 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>
JS Interoperabilidad sincrónica en componentes del lado cliente
Esta sección solo se aplica a los componentes del lado cliente.
Las llamadas de interoperabilidad de JS son asincrónicas, independientemente de si el código al que se llama es sincrónico o asincrónico. Las llamadas son asíncronas para garantizar que los componentes sean compatibles entre los modos de representación del lado del servidor y del lado del cliente. En el servidor, todas las llamadas de interoperabilidad de JS deben ser asincrónicas porque se envían a través de una conexión de red.
Si sabes con certeza que la aplicación solo se ejecuta en WebAssembly, puedes optar por realizar llamadas de interoperabilidad de JS sincrónicas. Esto tiene una sobrecarga ligeramente menor que la realización de llamadas asincrónicas y puede dar lugar a menos ciclos de representación porque no hay ningún estado intermedio mientras se esperan los resultados.
Para realizar una llamada sincrónica de JavaScript a .NET en un componente del lado cliente, use DotNet.invokeMethod
en lugar de DotNet.invokeMethodAsync
.
Las llamadas sincrónicas funcionan si:
Ubicación de JavaScript
Carga el código de JavaScript (JS) mediante cualquiera de los enfoques descritos en el artículo sobre la ubicación de JavaScript:
- Carga de un script en el marcado
<head>
(en general, no se recomienda) - Carga de un script en el marcado
<body>
- Carga de un script desde un archivo JavaScript externo (
.js
) colocado con un componente - Carga de un script desde un archivo JavaScript externo (
.js
) - Inserción de un script antes o después de después del inicio de Blazor
El uso de módulos JS para cargar JS se describe en este artículo en la sección Aislamiento de JavaScript en módulos de JavaScript.
Advertencia
Coloca solo una etiqueta <script>
en un archivo de componente (.razor
) si se garantiza que el componente adoptará la representación estática del lado servidor (SSR estática) porque la etiqueta <script>
no se puede actualizar de forma dinámica.
Advertencia
No coloques una etiqueta <script>
en un archivo de componente (.razor
), porque la etiqueta <script>
no se puede actualizar dinámicamente.
Aislamiento de JavaScript en módulos de JavaScript
Blazor permite el aislamiento de JavaScript (JS) en módulos de JavaScript estándar (especificación de ECMAScript). La carga de módulos de JavaScript funciona de la misma manera en Blazor que en otros tipos de aplicaciones web, y puede personalizar cómo se definen los módulos en su aplicación. Para obtener una guía sobre cómo usar módulos de JavaScript, consulta Documentación web de MDN: módulos de JavaScript.
El aislamiento de JS proporciona las siguientes ventajas:
- El JS importado no contamina el espacio de nombres global.
- No es necesario que los consumidores de una biblioteca y los componentes importen el código de JS relacionado.
Para obtener más información, consulta Llamada a funciones de JavaScript desde métodos de .NET en Blazor de ASP.NET Core.
La importación dinámica con el operador import()
se admite con ASP.NET Core y Blazor:
if ({CONDITION}) import("/additionalModule.js");
En el ejemplo anterior, el marcador de posición {CONDITION}
representa una comprobación condicional para determinar si se debe cargar el módulo.
Para la compatibilidad del explorador, consulta ¿Puedo utilizar: Módulos JavaScript: importación dinámica?
Evitar referencias de objetos circulares
Los objetos que contienen referencias circulares no se pueden serializar en el cliente para:
- Llamadas de método .NET.
- Llamadas de método JavaScript desde C# cuando el tipo de valor devuelto tiene referencias circulares.
Compatibilidad con matrices de bytes
Blazor admite la interoperabilidad de JavaScript de matriz de bytes optimizada (JS) que evita la codificación o descodificación de matrices de bytes en Base64. En el ejemplo siguiente se usa la interoperabilidad de JS para pasar una matriz de bytes a .NET.
Proporciona una función sendByteArray
de JS. La función se llama estáticamente, que incluye el parámetro de nombre de ensamblado en la llamada invokeMethodAsync
, mediante un botón en el componente y no devuelve un 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>
Nota:
Para obtener una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulta Ubicación de JavaScript en aplicaciones Blazor de ASP.NET Core.
CallDotnet8.razor
:
@page "/call-dotnet-8"
@using System.Text
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 8</PageTitle>
<h1>Call .NET Example 8</h1>
<p>
<button id="btn">Send Bytes</button>
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>
@code {
private IJSObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/Pages/CallDotnet8.razor.js");
await module.InvokeVoidAsync("addHandlers");
}
}
[JSInvokable]
public static Task<string> ReceiveByteArray(byte[] receivedBytes) =>
Task.FromResult(Encoding.UTF8.GetString(receivedBytes, 0,
receivedBytes.Length));
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
CallDotnet8.razor
:
@page "/call-dotnet-8"
@using System.Text
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 8</PageTitle>
<h1>Call .NET Example 8</h1>
<p>
<button id="btn">Send Bytes</button>
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>
@code {
private IJSObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/Pages/CallDotnet8.razor.js");
await module.InvokeVoidAsync("addHandlers");
}
}
[JSInvokable]
public static Task<string> ReceiveByteArray(byte[] receivedBytes) =>
Task.FromResult(Encoding.UTF8.GetString(receivedBytes, 0,
receivedBytes.Length));
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
CallDotNetExample8.razor
:
@page "/call-dotnet-example-8"
@using System.Text
<PageTitle>Call .NET 8</PageTitle>
<h1>Call .NET Example 8</h1>
<p>
<button onclick="sendByteArray()">Send Bytes</button>
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>
@code {
[JSInvokable]
public static Task<string> ReceiveByteArray(byte[] receivedBytes)
{
return Task.FromResult(
Encoding.UTF8.GetString(receivedBytes, 0, receivedBytes.Length));
}
}
CallDotNetExample8.razor
:
@page "/call-dotnet-example-8"
@using System.Text
<h1>Call .NET Example 8</h1>
<p>
<button onclick="sendByteArray()">Send Bytes</button>
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>
@code {
[JSInvokable]
public static Task<string> ReceiveByteArray(byte[] receivedBytes)
{
return Task.FromResult(
Encoding.UTF8.GetString(receivedBytes, 0, receivedBytes.Length));
}
}
Para obtener información sobre el uso de una matriz de bytes al llamar a JavaScript desde .NET, consulta Llamada a funciones de JavaScript desde métodos de .NET en ASP.NET Core Blazor.
Transmisión de JavaScript a .NET
Blazor admite el streaming de datos directamente desde JavaScript a .NET. Los flujos se solicitan mediante la interfaz Microsoft.JSInterop.IJSStreamReference
.
Microsoft.JSInterop.IJSStreamReference.OpenReadStreamAsync
devuelve un objeto Stream y usa los siguientes parámetros:
maxAllowedSize
: número máximo de bytes permitidos para la operación de lectura de JavaScript, cuyo valor predeterminado es 512 000 bytes si no se especifica.cancellationToken
: un objeto CancellationToken para cancelar la lectura.
En JavaScript:
function streamToDotNet() {
return new Uint8Array(10000000);
}
En código de 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);
En el ejemplo anterior:
JS
es una instancia de IJSRuntime insertada. El marco Blazor registra IJSRuntime.dataReferenceStream
se escribe en el disco (file.txt
) en la ruta de la carpeta temporal del usuario actual (GetTempPath).
En Llamada a funciones de JavaScript desde métodos de .NET en ASP.NET Core Blazor se describe la operación inversa, el streaming desde .NET a JavaScript mediante un objeto DotNetStreamReference.
En Cargas de archivos en ASP.NET Core Blazor se describe cómo cargar un archivo en Blazor. Para obtener un ejemplo de formularios que transmite datos <textarea>
en un componente del lado servidor, consulta Solución de problemas de formularios de ASP.NET Core Blazor.
Interoperabilidad [JSImport]
/[JSExport]
de JavaScript
Esta sección se aplica a los componentes del lado cliente.
Como alternativa a la interacción con JavaScript (JS) en los componentes del lado del cliente mediante el mecanismo de interoperabilidad de Blazor's JS basado en la interfaz IJSRuntime, existe una API de interoperabilidad JS[JSImport]
/[JSExport]
disponible para las aplicaciones orientadas a .NET 7 o posterior.
Para obtener más información, consulta Interoperabilidad JSImport/JSExport de JavaScript con ASP.NET Core Blazor.
Eliminación de referencias de objetos de interoperabilidad de JavaScript
Los ejemplos de los artículos de interoperabilidad de JavaScript (JS) muestran patrones típicos de eliminación de objetos:
Al llamar a .NET desde JS, como se describe en este artículo, elimina un DotNetObjectReference creado desde .NET o desde JS para evitar fugas de memoria de .NET.
Al llamar a JS desde .NET, como se describe en Llamada a funciones de JavaScript con métodos de .NET en ASP.NET CoreBlazor, elimina cualquier IJSObjectReference/IJSInProcessObjectReference/
JSObjectReference
que se haya creado desde JS para evitar la fuga de memoria de JS.
Las referencias de objeto de interoperabilidad JS se implementan como un mapa con clave por un identificador en el lado de la llamada de interoperabilidad JS que crea la referencia. Cuando se inicia la eliminación de objetos desde el lado de .NET o JS, Blazor quita la entrada del mapa y el objeto se puede recopilar como elemento no utilizado siempre que no haya ninguna otra referencia sólida al objeto presente.
Como mínimo, elimina siempre los objetos creados en .NET para evitar la pérdida de memoria administrada de .NET.
Tareas de limpieza del DOM durante la eliminación de componentes
Para más información, consulta Interoperabilidad de JavaScript en Blazor de ASP.NET Core (interoperabilidad de JS).
Llamadas de interoperabilidad de JavaScript sin un circuito
Para obtener más información, consulta Interoperabilidad de JavaScript en ASP.NET Core Blazor (interoperabilidad de JS).
Recursos adicionales
- Llamada a funciones de JavaScript con métodos de .NET en ASP.NET CoreBlazor
- Ejemplo de
InteropComponent.razor
(ramamain
del repositorio de GitHubdotnet/AspNetCore
): la ramamain
representa el desarrollo actual de la unidad de producto para la próxima versión de ASP.NET Core. Para seleccionar la rama para una versión diferente (por ejemplo,release/{VERSION}
, donde el marcador de posición es la{VERSION}
versión de lanzamiento), use la lista desplegable Cambiar ramas o etiquetas para seleccionar la rama. Para una rama que ya no existe, use la pestaña Etiquetas para buscar la API (por ejemplo,v7.0.0
). - Interacción con el DOM
- Repositorio de GitHub con ejemplos Blazor (
dotnet/blazor-samples
) (cómo descargar) - Gestión de errores en aplicaciones ASP.NET Core Blazor(sección de interoperabilidad de JavaScript)
- Mitigación de amenazas: métodos de .NET invocados desde el explorador