Partilhar via


Chamar métodos .NET a partir de funções JavaScript no ASP.NET Core Blazor

Note

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.

Warning

Esta versão do ASP.NET Core não é mais suportada. 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.

Este artigo explica como invocar métodos .NET do JavaScript (JS).

Para obter informações sobre como chamar JS funções do .NET, consulte Chamar funções JavaScript de métodos .NET no ASP.NET Core Blazor.

Invocar um método .NET estático

Para invocar um método .NET estático do JavaScript (JS), use as seguintes JS funções:

  • DotNet.invokeMethodAsync (recomendado): assíncrono para componentes do lado do servidor e do lado 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 .NET estático e quaisquer argumentos.

No exemplo a seguir:

  • O {PACKAGE ID/ASSEMBLY NAME} espaço reservado é a ID do pacote do projeto (<PackageId> no arquivo de projeto) para um nome de biblioteca ou de assemblagem de uma aplicação.
  • O {.NET METHOD ID} placeholder é o identificador do método .NET.
  • Os argumentos {ARGUMENTS} são opcionais e separados por vírgulas para serem passados para o método, cada um dos quais deve ser serializável em JSON.
DotNet.invokeMethodAsync('{PACKAGE ID/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.

Important

Para componentes do lado do servidor, recomendamos a função assíncrona (invokeMethodAsync) sobre a versão síncrona (invokeMethod).

O método .NET deve ser público, estático e ter o [JSInvokable] atributo.

No exemplo a seguir:

  • O {<T>} espaço reservado indica o tipo de retorno, que só é necessário para métodos que retornam um valor.
  • O marcador {.NET METHOD ID} é o identificador do método.
@code {
    [JSInvokable]
    public static Task{<T>} {.NET METHOD ID}()
    {
        ...
    }
}

Note

A chamada de métodos genéricos abertos não é suportada com métodos .NET estáticos, mas é suportada com métodos de instância. Para obter mais informações, consulte a seção Chamar métodos de classe genérica do .NET .

No componente a seguir, o ReturnArrayAsync método C# retorna uma int matriz. O [JSInvokable] atributo é aplicado ao método, o que torna o método invocável 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 addHandlersJS função adiciona um click evento ao botão. A returnArrayAsyncJS função é atribuída como o manipulador.

A returnArrayAsyncJS função chama o ReturnArrayAsync método .NET do componente, que registra o resultado no console de ferramentas de desenvolvedor da Web do navegador. BlazorSample é o nome do 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 addHandlersJS função adiciona um click evento ao botão. A returnArrayAsyncJS função é atribuída como o manipulador.

A returnArrayAsyncJS função chama o ReturnArrayAsync método .NET do componente, que registra o resultado no console de ferramentas de desenvolvedor da Web do navegador. BlazorSample é o nome do 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 <button> atributo HTML do onclick elemento é a atribuição do manipulador de eventos do JavaScript para processar onclick eventos, não o atributo de diretiva de click. A returnArrayAsyncJS função é atribuída como o manipulador.

A função a seguir returnArrayAsyncJS chama o ReturnArrayAsync método .NET do componente, que registra o resultado no console de ferramentas de desenvolvedor da Web do navegador. BlazorSample é o nome do assembly do aplicativo.

<script>
  window.returnArrayAsync = () => {
    DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
      .then(data => {
        console.log(data);
      });
    };
</script>

Note

Para obter orientações gerais sobre a JS localização e as nossas recomendações para aplicações de produção, consulte a localização de JavaScript em aplicações ASP.NET Core Blazor.

Quando o botão Trigger .NET static method é selecionado, os dados do array são exibidos na saída do console das ferramentas de desenvolvedor do navegador. O formato da saída difere ligeiramente entre os navegadores. A saída a seguir mostra o formato usado pelo Microsoft Edge:

Array(3) [ 11, 12, 13 ]

Passe dados para um método .NET ao chamar a invokeMethodAsync função passando os dados como argumentos.

Para demonstrar a passagem de dados para o .NET, passe uma posição inicial para o método ReturnArrayAsync onde 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 invokable ReturnArrayAsync do componente recebe a posição inicial e constrói a matriz a partir dela. A matriz é retornada para registro 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 aparece no console do navegador quando o botão é selecionado:

Array(3) [ 14, 15, 16 ]

O identificador do método .NET para a chamada de JS é o nome do método .NET, mas pode-se especificar um identificador diferente usando o construtor do atributo [JSInvokable]. No exemplo a seguir, DifferentMethodName é o identificador de método atribuído para o ReturnArrayAsync método:

[JSInvokable("DifferentMethodName")]

Na chamada para DotNet.invokeMethodAsync (componentes do lado do servidor ou do lado do cliente) ou DotNet.invokeMethod (apenas componentes do lado do cliente), chame DifferentMethodName para executar o método .NET ReturnArrayAsync:

  • DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
  • DotNet.invokeMethod('BlazorSample', 'DifferentMethodName'); (apenas componentes do lado do cliente)

Note

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 de C#. Métodos de codificação com async e await são típicos de métodos que usam a palavra-chave await para retornar o valor de operações assíncronas.

ReturnArrayAsync método composto com async e await palavras-chave:

[JSInvokable]
public static async Task<int[]> ReturnArrayAsync() => 
    await Task.FromResult(new int[] { 11, 12, 13 });

Para obter mais informações, consulte Programação assíncrona com async e await no guia C#.

Criar referências de objeto e dados JavaScript para passar para o .NET

Chame DotNet.createJSObjectReference(jsObject) para construir uma referência de objeto JS para que ela possa ser passada para .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 não serializável window para .NET, que o recebe no ReceiveWindowObject método C# como um IJSObjectReference:

DotNet.invokeMethodAsync('{PACKAGE ID/ASSEMBLY NAME}', 'ReceiveWindowObject', 
  DotNet.createJSObjectReference(window));
[JSInvokable]
public static void ReceiveWindowObject(IJSObjectReference objRef)
{
    ...
}

No exemplo anterior, o espaço reservado {PACKAGE ID/ASSEMBLY NAME} é a ID do pacote do projeto (<PackageId> no arquivo de projeto) para uma biblioteca ou nome de assembly para um aplicativo.

Note

O exemplo anterior não requer eliminação do JSObjectReference, pois uma referência ao window objeto não é mantida em JS.

Manter uma referência a um JSObjectReference requer descartá-la para evitar fuga de memória JS no cliente. O exemplo a seguir refatora o código anterior para capturar uma referência ao JSObjectReference, seguido por uma chamada para DotNet.disposeJSObjectReference() descartar a referência:

var jsObjectReference = DotNet.createJSObjectReference(window);

DotNet.invokeMethodAsync('{PACKAGE ID/ASSEMBLY NAME}', 'ReceiveWindowObject', jsObjectReference);

DotNet.disposeJSObjectReference(jsObjectReference);

No exemplo anterior, o espaço reservado {PACKAGE ID/ASSEMBLY NAME} é a ID do pacote do projeto (<PackageId> no arquivo de projeto) para uma biblioteca ou nome de assembly para um aplicativo.

Chame DotNet.createJSStreamReference(streamReference) para construir um fluxo de JS referência de modo que possa ser passado para o .NET, onde streamReference é um ArrayBuffer, Blob ou qualquer matriz tipada, como Uint8Array ou Float32Array, usada para criar o fluxo de referência de JS.

Invocar um método de instância .NET

Para invocar uma instância do método .NET do JavaScript (JS):

  • Passe a instância do .NET por referência a JS encapsulando a instância em um(a) DotNetObjectReference e chamando Create sobre ela.

  • Invoque um método de instância do .NET a partir de JS usando invokeMethodAsync (recomendado) ou invokeMethod (apenas para componentes do lado do cliente) passado para DotNetObjectReference. Passe o identificador do método .NET da instância e quaisquer argumentos. A instância do .NET também pode ser passada como um argumento ao invocar outros métodos .NET do JS.

    No exemplo a seguir:

    • dotNetHelper é um DotNetObjectReference.
    • O {.NET METHOD ID} placeholder é o identificador do método .NET.
    • Os argumentos {ARGUMENTS} são opcionais e separados por vírgulas para serem passados para o método, cada um dos quais deve ser serializável em JSON.
    dotNetHelper.invokeMethodAsync('{.NET METHOD ID}', {ARGUMENTS});
    

    Note

    invokeMethodAsync e invokeMethod não aceitam um parâmetro de nome de um assembly ao invocar um método de instância.

    invokeMethodAsync retorna um JS Promise que representa o resultado da operação. invokeMethod (somente componentes do lado do cliente) retorna o resultado da operação.

    Important

    Para componentes do lado do servidor, recomendamos a função assíncrona (invokeMethodAsync) sobre a versão síncrona (invokeMethod).

  • Elimine o DotNetObjectReference.

As seções a seguir deste artigo demonstram várias abordagens para invocar um método .NET de instância:

Evite cortar métodos .NET invocáveis em JavaScript

Esta seção se aplica a aplicativos do lado do cliente com compilação antecipada (AOT) e revinculação de tempo de execução habilitada.

Vários dos exemplos nas secções a seguir são baseados em uma abordagem baseada em instância de classe, onde o método .NET invocável em JavaScript marcado com o [JSInvokable] atributo é um membro de uma classe que não é um Razor componente. Quando esses métodos .NET estão localizados em um Razor componente, eles são protegidos contra revinculação/corte de tempo de execução. Para proteger os métodos .NET de corte fora dos componentes, implemente os métodos com o Razor no construtor da DynamicDependency classe, como o exemplo a seguir demonstra:

using System.Diagnostics.CodeAnalysis;
using Microsoft.JSInterop;

public class ExampleClass {

    [DynamicDependency(nameof(ExampleJSInvokableMethod))]
    public ExampleClass()
    {
    }

    [JSInvokable]
    public string ExampleJSInvokableMethod()
    {
        ...
    }
}

Para obter mais informações, consulte Preparar bibliotecas .NET para corte: DynamicDependency.

Passar a DotNetObjectReference para uma função JavaScript individual

O exemplo nesta seção demonstra como passar um DotNetObjectReference para uma função JavaScript (JS) individual.

A função a seguir sayHello1JS recebe um DotNetObjectReference e chama invokeMethodAsync para chamar o GetHelloMessage método .NET de um componente:

<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

Note

Para obter orientações gerais sobre a JS localização e as nossas recomendações para aplicações de produção, consulte a localização de JavaScript em aplicações ASP.NET Core Blazor.

No exemplo anterior, o nome dotNetHelper da variável é arbitrário e pode ser alterado para qualquer nome preferencial.

Para o seguinte componente:

  • O componente tem um JSmétodo .NET -invokable chamado GetHelloMessage.
  • Quando o Trigger .NET instance method botão é selecionado, a JS função sayHello1 é 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 .
  • A mensagem retornada de sayHello1 dentro result é exibida para o usuário.
  • Para evitar uma fuga de memória e permitir a coleta de lixo, a referência de objeto .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 dotNetHelper da variável é arbitrário e pode ser alterado para qualquer nome preferencial.

Use as seguintes orientações para passar argumentos para um método de instância:

Adicione parâmetros à chamada do método .NET. No exemplo a seguir, um nome é passado para o método. Adicione parâmetros adicionais à lista conforme necessário.

<script>
  window.sayHello2 = (dotNetHelper, name) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage', name);
  };
</script>

No exemplo anterior, o nome dotNetHelper da variável é arbitrário e pode ser alterado para qualquer nome preferencial.

Forneça a lista de parâmetros para o método .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 dotNetHelper da variável é arbitrário e pode ser alterado para qualquer nome preferencial.

Passar a 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 do método de ciclo de OnAfterRenderAsync vida para uma JS classe para várias funções usarem. Certifique-se de que o código .NET elimina o DotNetObjectReference, como mostra o exemplo a seguir.

No componente a seguir, os Trigger JS function botões chamam JS funções definindo a JSonclick propriedade, não o Blazor@onclick atributo de diretiva.

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 IJSRuntime injetada. IJSRuntime está registado pelo framework Blazor.
  • O nome dotNetHelper da variável é 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 um vazamento de memória.
  • JSDisconnectedException fica preso em caso de eliminação do módulo se o circuito Blazor for perdido. Se o código anterior for usado numa aplicação Blazor WebAssembly, não há conexão SignalR a perder, por isso pode-se remover o bloco de try-catch e deixar a linha que descarta o módulo (await module.DisposeAsync();). Para obter mais informações, consulte Interoperabilidade do JavaScript do ASP.NET Core Blazor (JS interop).

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 dotNetHelper da variável é 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 IJSRuntime injetada. IJSRuntime está registado pelo framework Blazor.
  • O nome dotNetHelper da variável é 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 um vazamento 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 GreetingHelpers classe é adicionada ao objeto window para definir globalmente a classe, o que permite Blazor localizar a classe para JS interop.
  • O nome dotNetHelper da variável é arbitrário e pode ser alterado para qualquer nome preferencial.

Note

Para obter orientações gerais sobre a JS localização e as nossas recomendações para aplicações de produção, consulte a localização de JavaScript em aplicações ASP.NET Core Blazor.

Chamar os métodos de classes genéricas do .NET

As funções JavaScript (JS) podem chamar métodos de classe genérica do .NET , onde uma JS função chama um método .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érica Value .
  • A classe tem dois métodos não genéricos marcados com o [JSInvokable] atributo, cada um com um parâmetro de tipo genérico chamado newValue:
    • Value atualiza síncronamente o valor de newValue de .
    • UpdateAsync atualiza de forma assíncrona o valor de Value a partir de newValue após criar uma tarefa aguardável com Task.Yield que rende de forma assíncrona de volta ao contexto atual quando aguardada.
  • Cada um dos métodos de classe escreve o tipo de TValue e o valor de Value no console. Escrever no console é apenas para fins de demonstração. Os aplicativos de produção geralmente evitam escrever para o console em favor do registo em log da aplicação. Para obter mais informações, consulte registo em ASP.NET Core Blazor e registo em .NET e ASP.NET Core.

Note

Tipos e métodos genéricos abertos não especificam tipos para variáveis de tipo. Por outro lado, os 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 com genéricos abertos é suportada. O uso de genéricos abertos não é suportado para invocações estáticas do método .NET, que foram descritas anteriormente neste artigo.

Para obter mais informações, consulte os seguintes artigos:

GenericType.cs:

using Microsoft.JSInterop;

public class GenericType<TValue>
{
    public TValue? Value { get; set; }

    [JSInvokable]
    public void Update(TValue newValue)
    {
        Value = newValue;

        Console.WriteLine($"Update: GenericType<{typeof(TValue)}>: {Value}");
    }

    [JSInvokable]
    public async Task UpdateAsync(TValue newValue)
    {
        await Task.Yield();
        Value = newValue;

        Console.WriteLine($"UpdateAsync: GenericType<{typeof(TValue)}>: {Value}");
    }
}

Na seguinte invokeMethodsAsync função:

  • Os métodos da classe de tipo genérica Update e UpdateAsync são chamados com argumentos que representam cadeias de caracteres e números.
  • Os componentes do lado do cliente suportam a chamada de métodos .NET de forma síncrona com invokeMethod. syncInterop Recebe um valor booleano indicando se a JS interoperabilidade está ocorrendo no cliente. Quando syncInterop é true, invokeMethod é chamado com segurança. Se o valor de syncInterop for false, somente a função assíncrona invokeMethodAsync será chamada porque a interoperabilidade JS está a ser executada num componente do lado do servidor.
  • Para fins de demonstração, a chamada da função DotNetObjectReference (invokeMethod ou invokeMethodAsync), o método .NET chamado (Update ou UpdateAsync) e o argumento são escritos no console. Os argumentos usam um número aleatório para permitir a correspondência da chamada da função JS com a invocação do método .NET (também registada no console do lado do .NET). O código de produção geralmente não grava no console, seja no cliente ou no servidor. As aplicações de produção geralmente dependem do logging. Para obter mais informações, consulte registo em ASP.NET Core Blazor e registo em .NET 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>

Note

Para obter orientações gerais sobre a JS localização e as nossas recomendações para aplicações de produção, consulte a localização de JavaScript em aplicações ASP.NET Core Blazor.

No componente seguinte GenericsExample:

  • A JS função invokeMethodsAsync é chamada quando o Invoke Interop botão é selecionado.
  • Um par de DotNetObjectReference tipos é criado e passado para a JS função como instâncias do GenericType como string e int.

GenericsExample.razor:

@page "/generics-example"
@implements IDisposable
@inject IJSRuntime JS

<p>
    <button @onclick="InvokeInterop">Invoke Interop</button>
</p>

<ul>
    <li>genericType1: @genericType1?.Value</li>
    <li>genericType2: @genericType2?.Value</li>
</ul>

@code {
    private GenericType<string> genericType1 = new() { Value = "string 0" };
    private GenericType<int> genericType2 = new() { Value = 0 };
    private DotNetObjectReference<GenericType<string>>? objRef1;
    private DotNetObjectReference<GenericType<int>>? objRef2;

    protected override void OnInitialized()
    {
        objRef1 = DotNetObjectReference.Create(genericType1);
        objRef2 = DotNetObjectReference.Create(genericType2);
    }

    public async Task InvokeInterop()
    {
        var syncInterop = OperatingSystem.IsBrowser();

        await JS.InvokeVoidAsync(
            "invokeMethodsAsync", syncInterop, objRef1, objRef2);
    }

    public void Dispose()
    {
        objRef1?.Dispose();
        objRef2?.Dispose();
    }
}
@page "/generics-example"
@implements IDisposable
@inject IJSRuntime JS

<p>
    <button @onclick="InvokeInterop">Invoke Interop</button>
</p>

<ul>
    <li>genericType1: @genericType1?.Value</li>
    <li>genericType2: @genericType2?.Value</li>
</ul>

@code {
    private GenericType<string> genericType1 = new() { Value = "string 0" };
    private GenericType<int> genericType2 = new() { Value = 0 };
    private DotNetObjectReference<GenericType<string>>? objRef1;
    private DotNetObjectReference<GenericType<int>>? objRef2;

    protected override void OnInitialized()
    {
        objRef1 = DotNetObjectReference.Create(genericType1);
        objRef2 = DotNetObjectReference.Create(genericType2);
    }

    public async Task InvokeInterop()
    {
        var syncInterop = OperatingSystem.IsBrowser();

        await JS.InvokeVoidAsync(
            "invokeMethodsAsync", syncInterop, objRef1, objRef2);
    }

    public void Dispose()
    {
        objRef1?.Dispose();
        objRef2?.Dispose();
    }
}

No exemplo anterior, JS é uma instância injetada IJSRuntime . IJSRuntime está registado pelo framework Blazor.

O seguinte demonstra a saída típica do exemplo anterior quando o Invoke Interop botão é selecionado em um componente do lado do cliente:

JS: invokeMethodAsync:Update('string 37802')
.NET: Atualize: GenericType<System.String>: string 37802
JS: invokeMethodAsync:UpdateAsync('string 53051')
JS: invokeMethod:Update('string 26784')
.NET: Atualização: GenericType<System.String>: string 26784
JS: invokeMethodAsync:Update(14107)
.NET: Atualização: GenericType<System.Int32>: 14107
JS: invokeMethodAsync:UpdateAsync(48995)
JS: invocarMétodo:Atualizar(12872)
.NET: Atualização: GenericType<System.Int32>: 12872
.NET: UpdateAsync: GenericType<System.String>: cadeia de caracteres 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) sobre a versão síncrona (invokeMethod).

Saída típica de um componente do lado do servidor:

JS: invokeMethodAsync:Update('string 34809')
.NET: Atualização: GenericType<System.String>: string 34809
JS: invokeMethodAsync:UpdateAsync('string 93059')
JS: invokeMethodAsync:Update(41997)
.NET: Atualizar: 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 os métodos assíncronos são executados e concluídos em uma ordem arbitrária , dependendo de vários fatores, incluindo o agendamento de threads e a velocidade de 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íncronas.

Exemplos de instâncias de classe

A seguinte função sayHello1JS:

  • Chama o método .NET GetHelloMessage no DotNetObjectReference passado como argumento.
  • Retorna a mensagem de GetHelloMessage para o chamador sayHello1.
<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

Note

Para obter orientações gerais sobre a JS localização e as nossas recomendações para aplicações de produção, consulte a localização de JavaScript em aplicações ASP.NET Core Blazor.

No exemplo anterior, o nome dotNetHelper da variável é arbitrário e pode ser alterado para qualquer nome preferencial.

A seguinte classe HelloHelper possui um método .NET JS invocável chamado GetHelloMessage. Quando HelloHelper é criado, o nome na propriedade Name é utilizado para devolver 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 seguinte JsInteropClasses3 invoca a função JSsayHello1 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 uma fuga de memória e permitir a coleta de lixo, a referência de objeto .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 seguinte, 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 Name campo. Depois que o botão for selecionado, Hello, Amy Pond! será exibido na interface do usuário:

Exemplo do componente 'CallDotNetExample4' renderizado

O padrão anterior mostrado na JsInteropClasses3 classe 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 uma fuga de memória e permitir a coleta de lixo, a referência de objeto .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 name campo.

No componente anterior, a referência de objeto .NET é descartada. Se uma classe ou componente não descartar o DotNetObjectReference, descartá-lo no cliente chamando dispose no DotNetObjectReference passado:

window.{JS FUNCTION NAME} = (dotNetHelper) => {
  dotNetHelper.invokeMethodAsync('{.NET METHOD ID}');
  dotNetHelper.dispose();
}

No exemplo anterior:

  • O {JS FUNCTION NAME} espaço reservado é o nome da JS função.
  • O nome dotNetHelper da variável é arbitrário e pode ser alterado para qualquer nome preferencial.
  • O {.NET METHOD ID} placeholder é o identificador do método .NET.

Classe auxiliar para métodos .NET de instâncias de componentes

Uma classe auxiliar pode invocar um método de instância .NET como 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 usando simultaneamente o mesmo componente.

No exemplo a seguir:

  • O componente contém vários ListItem1 componentes.
  • Cada ListItem1 componente é composto por uma mensagem e um botão.
  • Quando um botão de componente ListItem1 é selecionado, o método ListItem1 desse UpdateMessage altera o texto do item da lista e oculta o botão.

A seguinte classe MessageUpdateInvokeHelper mantém um método .NET invocável, JS, para invocar o UpdateMessageCaller 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 a seguir updateMessageCallerJS invoca o UpdateMessageCaller método .NET.

<script>
  window.updateMessageCaller = (dotNetHelper) => {
    dotNetHelper.invokeMethodAsync('UpdateMessageCaller');
    dotNetHelper.dispose();
  }
</script>

Note

Para obter orientações gerais sobre a JS localização e as nossas recomendações para aplicações de produção, consulte a localização de JavaScript em aplicações ASP.NET Core Blazor.

No exemplo anterior, o nome dotNetHelper da variável é arbitrário e pode ser alterado para qualquer nome preferencial.

O componente a seguir ListItem1 é um componente compartilhado que pode ser usado qualquer número de vezes em um componente pai e cria itens de lista (<li>...</li>) para uma lista HTML (<ul>...</ul> ou <ol>...</ol>). Cada ListItem1 instância de componente estabelece uma instância de MessageUpdateInvokeHelper com um Action definido para o seu UpdateMessage método.

Quando o botão ListItem1 de um InteropCall componente é selecionado, updateMessageCaller é invocado com um DotNetObjectReference criado para a instância MessageUpdateInvokeHelper. Isto permite que o quadro chame UpdateMessageCaller na instância ListItem1MessageUpdateInvokeHelper. 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 está definido em UpdateMessage. Se StateHasChanged não for chamado, Blazor não tem como saber que a interface do usuário deve ser atualizada quando o Action é invocado.

O componente pai a seguir inclui quatro itens de lista, cada um sendo 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 InteropCall botão:

  • O segundo ListItem1 componente exibiu a UpdateMessage Called! mensagem.
  • O InteropCall botão do segundo ListItem1 componente não está visível porque a propriedade CSS display do botão está definida como none.

Exemplo do componente 'CallDotNetExample6' renderizado

Método .NET de instância de componente chamado a partir de DotNetObjectReference atribuído a uma propriedade de elemento.

A atribuição de a DotNetObjectReference a uma propriedade de um elemento HTML permite chamar métodos .NET em uma instância de componente:

Semelhante à abordagem descrita na seção Component instance .NET method helper class , essa abordagem é útil em cenários onde 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 usando simultaneamente o mesmo componente.
  • O método .NET é invocado a partir de um JS evento (por exemplo, onclick), não de um Blazor evento (por exemplo, @onclick).

No exemplo a seguir:

  • O componente contém vários ListItem2 componentes, que é um componente compartilhado.
  • Cada ListItem2 componente é composto por uma mensagem de item de lista <span> e uma segunda <span> com uma propriedade CSS display definida para exibição inline-block.
  • Quando um item de ListItem2 lista de componentes é selecionado, esse ListItem2método altera UpdateMessage o texto do item de lista no primeiro <span> e oculta o segundo <span> definindo sua display propriedade como none.

A função a seguir assignDotNetHelperJS atribui o DotNetObjectReference a um elemento em uma propriedade chamada dotNetHelper. A função a seguir interopCallJS usa o DotNetObjectReference para o elemento passado para invocar um método .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>

Note

Para obter orientações gerais sobre a JS localização e as nossas recomendações para aplicações de produção, consulte a localização de JavaScript em aplicações ASP.NET Core Blazor.

No exemplo anterior, o nome dotNetHelper da variável é arbitrário e pode ser alterado para qualquer nome preferencial.

O componente a seguir ListItem2 é um componente compartilhado que pode ser usado qualquer número de 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 assignDotNetHelperJS em OnAfterRenderAsync com uma referência de elemento (o primeiro <span> elemento do item de lista) e a instância de componente como um DotNetObjectReference.

Quando a mensagem de um ListItem2 componente é selecionada, <span> é invocado passando o interopCall elemento como um parâmetro (<span>), que invoca o método .NET this. No UpdateMessage, StateHasChanged é chamado para atualizar a interface do usuário quando message é definido e a display propriedade do segundo <span> é atualizada. Se StateHasChanged não for chamado, Blazor não tem como saber que a interface do usuário deve ser atualizada quando o método é invocado.

O DotNetObjectReference é eliminado quando o componente é eliminado.

ListItem2.razor:

@inject IJSRuntime JS
@implements IAsyncDisposable

<li>
    <span style="font-weight:bold;color:@color" @ref="elementRef"
        @onclick="CallJSToInvokeDotnet">
        @message
    </span>
    <span style="display:@display">
        Not Updated Yet!
    </span>
</li>

@code {
    private IJSObjectReference? module;
    private DotNetObjectReference<ListItem2>? objRef;
    private ElementReference elementRef;
    private string display = "inline-block";
    private string message = "Select one of these list items.";
    private string color = "initial";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            module = await JS.InvokeAsync<IJSObjectReference>("import",
                "./Components/ListItem2.razor.js");

            objRef = DotNetObjectReference.Create(this);
            await module.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
        }
    }

    public async Task CallJSToInvokeDotnet()
    {
        if (module is not null)
        {
            await module.InvokeVoidAsync("interopCall", elementRef);
        }
    }

    [JSInvokable]
    public void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        color = "MediumSeaGreen";
        StateHasChanged();
    }

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            try
            {
                await module.DisposeAsync();
            }
            catch (JSDisconnectedException)
            {
            }
        }

        objRef?.Dispose();
    }
}
@inject IJSRuntime JS
@implements IAsyncDisposable

<li>
    <span style="font-weight:bold;color:@color" @ref="elementRef"
        @onclick="CallJSToInvokeDotnet">
        @message
    </span>
    <span style="display:@display">
        Not Updated Yet!
    </span>
</li>

@code {
    private IJSObjectReference? module;
    private DotNetObjectReference<ListItem2>? objRef;
    private ElementReference elementRef;
    private string display = "inline-block";
    private string message = "Select one of these list items.";
    private string color = "initial";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            module = await JS.InvokeAsync<IJSObjectReference>("import",
                "./Components/ListItem2.razor.js");

            objRef = DotNetObjectReference.Create(this);
            await module.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
        }
    }

    public async Task CallJSToInvokeDotnet()
    {
        if (module is not null)
        {
            await module.InvokeVoidAsync("interopCall", elementRef);
        }
    }

    [JSInvokable]
    public void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        color = "MediumSeaGreen";
        StateHasChanged();
    }

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            try
            {
                await module.DisposeAsync();
            }
            catch (JSDisconnectedException)
            {
            }
        }

        objRef?.Dispose();
    }
}
@inject IJSRuntime JS

<li>
    <span @ref="elementRef" onclick="interopCall(this)">@message</span>
    <span style="display:@display">Not Updated Yet!</span>
</li>

@code {
    private DotNetObjectReference<ListItem2>? objRef;
    private ElementReference elementRef;
    private string display = "inline-block";
    private string message = "Select one of these list items.";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            objRef = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
        }
    }

    [JSInvokable]
    public void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }

    public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS

<li>
    <span @ref="elementRef" onclick="interopCall(this)">@message</span>
    <span style="display:@display">Not Updated Yet!</span>
</li>

@code {
    private DotNetObjectReference<ListItem2>? objRef;
    private ElementReference elementRef;
    private string display = "inline-block";
    private string message = "Select one of these list items.";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            objRef = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
        }
    }

    [JSInvokable]
    public void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }

    public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS

<li>
    <span @ref="elementRef" onclick="interopCall(this)">@message</span>
    <span style="display:@display">Not Updated Yet!</span>
</li>

@code {
    private DotNetObjectReference<ListItem2> objRef;
    private ElementReference elementRef;
    private string display = "inline-block";
    private string message = "Select one of these list items.";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            objRef = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
        }
    }

    [JSInvokable]
    public void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }

    public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS

<li>
    <span @ref="elementRef" onclick="interopCall(this)">@message</span>
    <span style="display:@display">Not Updated Yet!</span>
</li>

@code {
    private DotNetObjectReference<ListItem2> objRef;
    private ElementReference elementRef;
    private string display = "inline-block";
    private string message = "Select one of these list items.";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            objRef = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
        }
    }

    [JSInvokable]
    public void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }

    public void Dispose() => objRef?.Dispose();
}

O componente pai a seguir inclui quatro itens de lista, cada um sendo 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>

Interoperação síncrona em componentes cliente

Esta seção só se aplica a componentes do lado do cliente.

As chamadas de interop JS são assíncronas, independentemente de o código chamado ser síncrono ou assíncrono. As chamadas são assíncronas para garantir que os componentes sejam compatíveis entre os modos de renderização do lado do servidor e do lado do cliente. No servidor, todas as chamadas de interoperabilidade JS devem ser assíncronas porque são enviadas por uma conexão de rede.

Se tiveres a certeza de que o teu componente só funciona em WebAssembly, podes optar por realizar chamadas de interoperabilidade JS síncronas. Isso tem um pouco menos de sobrecarga do que fazer chamadas assíncronas e pode resultar em menos ciclos de renderização porque não há nenhum estado intermediário enquanto aguarda resultados.

Para fazer uma chamada síncrona de JavaScript para .NET em um componente do lado do cliente, use DotNet.invokeMethod em vez de DotNet.invokeMethodAsync.

As chamadas síncronas funcionam se:

  • O componente só é renderizado para execução em WebAssembly.
  • A função chamada retorna um valor de forma síncrona. A função não é um método async e não retorna um Task .NET ou JavaScript Promise.

Localização de JavaScript

Carregue o código JavaScript (JS) usando qualquer uma das abordagens descritas pelo artigo do sobre localização JavaScript:

A utilização de módulos JS para efetuar o carregamento de JS é explicada na secção Isolamento de JavaScript em módulos JavaScript deste artigo.

Warning

Coloque apenas uma tag <script> em um arquivo de componente (.razor) se o componente tiver a garantia de adotar renderização estática do lado do servidor (SSR estático) porque a tag <script> não pode ser atualizada dinamicamente.

Warning

Não coloque uma tag <script> em um arquivo de componente (.razor) porque a tag <script> não pode ser atualizada dinamicamente.

Isolamento do JavaScript nos módulos de JavaScript

Blazor permite o isolamento de JavaScript (JS) em módulos JavaScript padrão (especificação ECMAScript). O carregamento de módulos JavaScript funciona da mesma forma no Blazor que em 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, consulte MDN Web Docs: JavaScript modules.

JS isolamento oferece os seguintes benefícios:

  • Os JS importados não poluem mais o namespace global.
  • Os consumidores de uma biblioteca e dos seus componentes não são obrigados a importar o JS relacionado.

Para obter mais informações, veja Executar funções JavaScript a partir de métodos .NET no ASP.NET Core Blazor.

Importação dinâmica com o operador import() é suportada 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 informações sobre a compatibilidade do navegador, consulte Posso usar: módulos JavaScript: importação dinâmica.

Evitar referências a objetos circulares

Objetos que contêm referências circulares não podem ser serializados no cliente por qualquer um dos seguintes motivos:

  • Chamadas de métodos do .NET.
  • Quando chamadas de métodos JavaScript são feitas a partir de C#, e o tipo de retorno tem referências circulares.

Suporte a matriz de bytes

Blazor suporta uma interoperabilidade otimizada de matriz de bytes JavaScript (JS) que evita a codificação/decodificação de matrizes de bytes em Base64. O exemplo a seguir usa JS interop para passar uma matriz de bytes para .NET.

Forneça uma função sendByteArrayJS. A função é chamada estaticamente, incluindo o parâmetro "assembly name" na chamada invokeMethodAsync, por 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>

Note

Para obter orientações gerais sobre a JS localização e as nossas recomendações para aplicações de produção, consulte a localização de JavaScript em aplicações 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 do .NET, consulte Chamar funções JavaScript de métodos .NET no ASP.NET Core Blazor.

Transmissão de JavaScript para .NET

Blazor suporta streaming de dados diretamente do JavaScript para .NET. Os fluxos são solicitados usando a Microsoft.JSInterop.IJSStreamReference interface.

Microsoft.JSInterop.IJSStreamReference.OpenReadStreamAsync retorna a Stream e usa os seguintes parâmetros:

  • maxAllowedSize: Número máximo de bytes permitido para a operação de leitura do JavaScript, cujo padrão é 512.000 bytes se não for especificado.
  • cancellationToken: A CancellationToken para cancelar a leitura.

Em 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 IJSRuntime injetada. IJSRuntime está registado pelo framework Blazor.
  • O dataReferenceStream é gravado no disco (file.txt) no caminho da pasta temporária do usuário atual (GetTempPath).

Chamar funções JavaScript a partir de métodos .NET no ASP.NET Core Blazor cobre a operação inversa, transmitindo do .NET para o JavaScript usando um DotNetStreamReference.

Carregamentos de ficheiros no ASP.NET Core Blazor aborda como carregar um ficheiro no Blazor. Para obter um exemplo de formulários que transmite dados em um componente do lado do <textarea> servidor, consulte Solucionar problemas de formulários ASP.NET CoreBlazor.

Interoperabilidade JavaScript [JSImport]/[JSExport]

Esta seção se aplica aos componentes do lado do cliente.

Como alternativa à interação com JavaScript (JS) em componentes do lado do cliente usando o mecanismo de interoperabilidade Blazor do JSbaseado na interface IJSRuntime, uma API de interoperabilidade JS[JSImport]/[JSExport] está disponível para aplicativos destinados ao .NET 7 ou posterior.

Para obter mais informações, consulte Interoperabilidade JSImport/JSExport JavaScript com ASP.NET Core Blazor.

Eliminação de referências de interoperabilidade de objetos JavaScript

Exemplos ao longo dos artigos de interoperabilidade JavaScript (JS) demonstram padrões típicos de descarte de objetos:

JS referências de objeto de interoperabilidade são implementadas como um mapa chaveado por um identificador no lado da chamada de interoperabilidade que cria a referência de JS. Quando o descarte de objeto é iniciado do lado .NET ou JS, o Blazor remove a entrada do mapa e o objeto pode ser coletado como lixo, contanto que nenhuma outra referência forte ao objeto esteja presente.

No mínimo, sempre descarte objetos criados no lado do .NET para evitar fugas de memória gerida pelo .NET.

Tarefas de limpeza do DOM durante o descarte de componentes

Para obter mais informações, consulte Interoperabilidade do JavaScript do ASP.NET Core Blazor (JS interop).

Chamadas de interoperação do JavaScript sem circuito

Para obter mais informações, consulte Interoperabilidade do JavaScript do ASP.NET Core Blazor (JS interop).

Recursos adicionais