Condividi tramite


Chiamare metodi .NET da funzioni JavaScript in ASP.NET Core Blazor

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Avviso

Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Questo articolo illustra come richiamare i metodi .NET da JavaScript (JS).

Per informazioni su come chiamare JS le funzioni da .NET, vedere Chiamare le funzioni JavaScript dai metodi .NET in ASP.NET Core Blazor.

Richiamare un metodo .NET statico

Per richiamare un metodo .NET statico da JavaScript (JS), usare le JS funzioni:

  • DotNet.invokeMethodAsync (scelta consigliata): asincrona per i componenti lato server e lato client.
  • DotNet.invokeMethod: sincrono solo per i componenti lato client.

Passare il nome dell'assembly contenente il metodo , l'identificatore del metodo .NET statico ed eventuali argomenti.

Nell'esempio seguente :

  • Il {ASSEMBLY NAME} segnaposto è il nome dell'assembly dell'app.
  • Il {.NET METHOD ID} segnaposto è l'identificatore del metodo .NET.
  • Il {ARGUMENTS} segnaposto è un argomento facoltativo delimitato da virgole da passare al metodo , ognuno dei quali deve essere serializzabile in JSON.
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});

DotNet.invokeMethodAsync restituisce un oggetto JS Promise che rappresenta il risultato dell'operazione. DotNet.invokeMethod (componenti lato client) restituisce il risultato dell'operazione.

Importante

Per i componenti lato server, è consigliabile usare la funzione asincrona (invokeMethodAsync) sulla versione sincrona (invokeMethod).

Il metodo .NET deve essere pubblico, statico e avere l'attributo [JSInvokable].

Nell'esempio seguente :

  • Il {<T>} segnaposto indica il tipo restituito, obbligatorio solo per i metodi che restituiscono un valore.
  • Il {.NET METHOD ID} segnaposto è l'identificatore del metodo.
@code {
    [JSInvokable]
    public static Task{<T>} {.NET METHOD ID}()
    {
        ...
    }
}

Nota

La chiamata di metodi generici aperti non è supportata con metodi .NET statici, ma è supportata con i metodi di istanza. Per altre informazioni, vedere la sezione Chiamare i metodi di classe generica .NET.

Nel componente seguente il ReturnArrayAsync metodo C# restituisce una int matrice. L'attributo [JSInvokable] viene applicato al metodo , che rende il metodo richiamabile da 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);
}

La addHandlersJS funzione aggiunge un click evento al pulsante. La returnArrayAsyncJS funzione viene assegnata come gestore.

La returnArrayAsyncJS funzione chiama il ReturnArrayAsync metodo .NET del componente, che registra il risultato nella console degli strumenti di sviluppo Web del browser. BlazorSample è il nome dell'assembly dell'app.

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);
}

La addHandlersJS funzione aggiunge un click evento al pulsante. La returnArrayAsyncJS funzione viene assegnata come gestore.

La returnArrayAsyncJS funzione chiama il ReturnArrayAsync metodo .NET del componente, che registra il risultato nella console degli strumenti di sviluppo Web del browser. BlazorSample è il nome dell'assembly dell'app.

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 });
    }
}

L'attributo <button> HTML dell'elemento onclick è l'assegnazione del gestore eventi javaScript onclick per l'elaborazione click degli eventi, non Blazorl'attributo di direttiva di @onclick . La returnArrayAsyncJS funzione viene assegnata come gestore.

La funzione seguente returnArrayAsyncJS chiama il ReturnArrayAsync metodo .NET del componente, che registra il risultato nella console degli strumenti di sviluppo Web del browser. BlazorSample è il nome dell'assembly dell'app.

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

Nota

Per indicazioni generali sulla JS posizione e i suggerimenti per le app di produzione, vedere Posizione JavaScript nelle app ASP.NET CoreBlazor.

Quando il Trigger .NET static method pulsante è selezionato, l'output della console degli strumenti di sviluppo del browser visualizza i dati della matrice. Il formato dell'output è leggermente diverso tra i browser. L'output seguente mostra il formato usato da Microsoft Edge:

Array(3) [ 11, 12, 13 ]

Passare i dati a un metodo .NET quando si chiama la invokeMethodAsync funzione passando i dati come argomenti.

Per illustrare il passaggio di dati a .NET, passare una posizione iniziale al ReturnArrayAsync metodo in cui viene richiamato il metodo in 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>

Il metodo richiamabile ReturnArrayAsync del componente riceve la posizione iniziale e costruisce la matrice da essa. La matrice viene restituita per la registrazione nella console:

[JSInvokable]
public static Task<int[]> ReturnArrayAsync(int startPosition) => 
    Task.FromResult(Enumerable.Range(startPosition, 3).ToArray());

Dopo aver ricompilato l'app e aver aggiornato il browser, l'output seguente viene visualizzato nella console del browser quando il pulsante è selezionato:

Array(3) [ 14, 15, 16 ]

L'identificatore del metodo .NET per la JS chiamata è il nome del metodo .NET, ma è possibile specificare un identificatore diverso usando il costruttore dell'attributo[JSInvokable]. Nell'esempio seguente è DifferentMethodName l'identificatore del metodo assegnato per il ReturnArrayAsync metodo :

[JSInvokable("DifferentMethodName")]

Nella chiamata a DotNet.invokeMethodAsync (componenti lato server o lato client) o DotNet.invokeMethod (solo componenti lato client), chiamare DifferentMethodName per eseguire il ReturnArrayAsync metodo .NET:

  • DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
  • DotNet.invokeMethod('BlazorSample', 'DifferentMethodName'); (solo componenti lato client)

Nota

L'esempio ReturnArrayAsync di metodo in questa sezione restituisce il risultato di un oggetto Task senza l'uso di parole chiave e await C# async esplicite. I metodi di codifica con async e await sono tipici di metodi che usano la await parola chiave per restituire il valore delle operazioni asincrone.

ReturnArrayAsync metodo composto con async parole chiave e await :

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

Per altre informazioni, vedere Programmazione asincrona con async e await nella guida di C#.

Creare riferimenti a oggetti e dati JavaScript da passare a .NET

Chiamare DotNet.createJSObjectReference(jsObject) per costruire un riferimento a un JS oggetto in modo che possa essere passato a .NET, dove jsObject viene JS Object usato per creare il riferimento all'oggetto JS . Nell'esempio seguente viene passato un riferimento all'oggetto non serializzabile window a .NET, che lo riceve nel ReceiveWindowObject metodo C# come IJSObjectReference:

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

Nell'esempio precedente il {ASSEMBLY NAME} segnaposto è lo spazio dei nomi dell'app.

Nota

L'esempio precedente non richiede l'eliminazione JSObjectReferencedi , come riferimento all'oggetto window non viene mantenuto in JS.

Per mantenere un riferimento a un oggetto JSObjectReference è necessario eliminarlo per evitare perdite JS di memoria nel client. Nell'esempio seguente viene eseguito il refactoring del codice precedente per acquisire un riferimento a JSObjectReference, seguito da una chiamata a per DotNet.disposeJSObjectReference() eliminare il riferimento:

var jsObjectReference = DotNet.createJSObjectReference(window);

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

DotNet.disposeJSObjectReference(jsObjectReference);

Nell'esempio precedente il {ASSEMBLY NAME} segnaposto è lo spazio dei nomi dell'app.

Chiamare DotNet.createJSStreamReference(streamReference) per costruire un JS riferimento al flusso in modo che possa essere passato a .NET, dove streamReference è una ArrayBuffermatrice , Blobo qualsiasi matrice tipizzata, ad esempio Uint8Array o Float32Array, usata per creare il riferimento al JS flusso.

Richiamare un metodo .NET di istanza

Per richiamare un metodo .NET dell'istanza da JavaScript (JS):

  • Passare l'istanza .NET per riferimento a JS eseguendo il wrapping dell'istanza in un DotNetObjectReference oggetto e chiamandola Create .

  • Richiamare un metodo di istanza .NET usando JS invokeMethodAsync (scelta consigliata) o invokeMethod (solo componenti lato client) dall'oggetto passato DotNetObjectReference. Passare l'identificatore del metodo .NET dell'istanza e qualsiasi argomento. L'istanza di .NET può anche essere passata come argomento quando si richiamano altri metodi .NET da JS.

    Nell'esempio seguente :

    • dotNetHelper è un oggetto DotNetObjectReference.
    • Il {.NET METHOD ID} segnaposto è l'identificatore del metodo .NET.
    • Il {ARGUMENTS} segnaposto è un argomento facoltativo delimitato da virgole da passare al metodo , ognuno dei quali deve essere serializzabile in JSON.
    dotNetHelper.invokeMethodAsync('{.NET METHOD ID}', {ARGUMENTS});
    

    Nota

    invokeMethodAsync e invokeMethod non accettano un parametro del nome dell'assembly quando si richiama un metodo di istanza.

    invokeMethodAsync restituisce un oggetto JS Promise che rappresenta il risultato dell'operazione. invokeMethod (solo componenti lato client) restituisce il risultato dell'operazione.

    Importante

    Per i componenti lato server, è consigliabile usare la funzione asincrona (invokeMethodAsync) sulla versione sincrona (invokeMethod).

  • Eliminare l'oggetto DotNetObjectReference.

Le sezioni seguenti di questo articolo illustrano diversi approcci per richiamare un metodo .NET di istanza:

Evitare di tagliare i metodi JavaScript-invokable .NET

Questa sezione si applica alle app sul lato client con compilazione anticipata (AOT) e ricollegamento del runtime abilitato.

Diversi esempi nelle sezioni seguenti sono basati su un approccio all'istanza di classe, in cui il metodo .NET richiamabile JavaScript contrassegnato con l'attributo [JSInvokable] è un membro di una classe che non è un Razor componente. Quando tali metodi .NET si trovano in un Razor componente, sono protetti dal ricollegamento/taglio in fase di esecuzione. Per proteggere i metodi .NET dal taglio all'esterno dei Razor componenti, implementare i metodi con l'attributo DynamicDependency nel costruttore della classe, come illustrato nell'esempio seguente:

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

public class ExampleClass {

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

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

Per altre informazioni, vedere Preparare le librerie .NET per il taglio: DynamicDependency.

Passare un DotNetObjectReference oggetto a una singola funzione JavaScript

L'esempio in questa sezione illustra come passare un DotNetObjectReference oggetto a una singola funzione JavaScript (JS).

La funzione seguente sayHello1JS riceve un e DotNetObjectReference chiama invokeMethodAsync per chiamare il GetHelloMessage metodo .NET di un componente:

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

Nota

Per indicazioni generali sulla JS posizione e i suggerimenti per le app di produzione, vedere Posizione JavaScript nelle app ASP.NET CoreBlazor.

Nell'esempio precedente il nome dotNetHelper della variabile è arbitrario e può essere modificato in qualsiasi nome preferito.

Per il componente seguente:

  • Il componente ha un JSmetodo .NET richiamabile denominato GetHelloMessage.
  • Quando il Trigger .NET instance method pulsante è selezionato, la JS funzione sayHello1 viene chiamata con .DotNetObjectReference
  • sayHello1:
    • Chiama GetHelloMessage e riceve il risultato del messaggio.
    • Restituisce il risultato del messaggio al metodo chiamante TriggerDotNetInstanceMethod .
  • Il messaggio restituito da sayHello1 in result viene visualizzato all'utente.
  • Per evitare una perdita di memoria e consentire l'operazione di Garbage Collection, il riferimento all'oggetto .NET creato da DotNetObjectReference viene eliminato nel Dispose metodo .

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();
    }
}

Nell'esempio precedente il nome dotNetHelper della variabile è arbitrario e può essere modificato in qualsiasi nome preferito.

Usare le indicazioni seguenti per passare argomenti a un metodo di istanza:

Aggiungere parametri alla chiamata al metodo .NET. Nell'esempio seguente viene passato un nome al metodo . Aggiungere altri parametri all'elenco in base alle esigenze.

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

Nell'esempio precedente il nome dotNetHelper della variabile è arbitrario e può essere modificato in qualsiasi nome preferito.

Specificare l'elenco di parametri al metodo .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();
    }
}

Nell'esempio precedente il nome dotNetHelper della variabile è arbitrario e può essere modificato in qualsiasi nome preferito.

Passare a DotNetObjectReference una classe con più funzioni JavaScript

L'esempio in questa sezione illustra come passare un DotNetObjectReference oggetto a una classe JavaScript (JS) con più funzioni.

Creare e passare un DotNetObjectReference oggetto dal metodo del OnAfterRenderAsync ciclo di vita a una JS classe per più funzioni da usare. Assicurarsi che il codice .NET elimini , DotNetObjectReferencecome illustrato nell'esempio seguente.

Nel componente seguente i pulsanti chiamano le Trigger JS function funzioni impostando la JSonclick proprietà , non Blazorl'attributo di direttiva.@onclick JS

CallDotNetExampleOneHelper.razor:

@page "/call-dotnet-example-one-helper"
@implements IAsyncDisposable
@inject IJSRuntime JS

<PageTitle>Call .NET Example</PageTitle>

<h1>Pass <code>DotNetObjectReference</code> to a JavaScript class</h1>

<p>
    <label>
        Message: <input @bind="name" />
    </label>
</p>

<p>
    <button id="sayHelloBtn">
        Trigger JS function <code>sayHello</code>
    </button>
</p>

<p>
    <button id="welcomeVisitorBtn">
        Trigger JS function <code>welcomeVisitor</code>
    </button>
</p>

@code {
    private IJSObjectReference? module;
    private string? name;
    private DotNetObjectReference<CallDotNetExampleOneHelper>? dotNetHelper;

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

            dotNetHelper = DotNetObjectReference.Create(this);
            await module.InvokeVoidAsync("GreetingHelpers.setDotNetHelper", 
                dotNetHelper);

            await module.InvokeVoidAsync("addHandlers");
        }
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

    [JSInvokable]
    public string GetWelcomeMessage() => $"Welcome, {name}!";

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

        dotNetHelper?.Dispose();
    }
}

Nell'esempio precedente:

  • JS è un'istanza inserita IJSRuntime . IJSRuntime viene registrato dal Blazor framework.
  • Il nome dotNetHelper della variabile è arbitrario e può essere modificato in qualsiasi nome preferito.
  • Il componente deve eliminare in modo esplicito l'oggetto per consentire l'operazione DotNetObjectReference di Garbage Collection e impedire una perdita di memoria.
  • JSDisconnectedException viene intrappolato durante l'eliminazione del modulo nel caso Blazorin cui il circuito venga SignalR perso. Se il codice precedente viene usato in un'appBlazor WebAssembly, non c'è alcuna SignalR connessione da perdere, quindi è possibile rimuovere ilcatch try-blocco e lasciare la riga che elimina il modulo ().await module.DisposeAsync(); Per altre informazioni, vedere ASP.NET Core JavaScript interoperabilità (interoperabilità).For more information, see ASP.NET Core Blazor JavaScript interoperability (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);
}

Nell'esempio precedente il nome dotNetHelper della variabile è arbitrario e può essere modificato in qualsiasi nome preferito.

@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();
    }
}

Nell'esempio precedente:

  • JS è un'istanza inserita IJSRuntime . IJSRuntime viene registrato dal Blazor framework.
  • Il nome dotNetHelper della variabile è arbitrario e può essere modificato in qualsiasi nome preferito.
  • Il componente deve eliminare in modo esplicito l'oggetto per consentire l'operazione DotNetObjectReference di Garbage Collection e impedire una perdita di memoria.
<script>
  class GreetingHelpers {
    static dotNetHelper;

    static setDotNetHelper(value) {
      GreetingHelpers.dotNetHelper = value;
    }

    static async sayHello() {
      const msg = 
        await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetHelloMessage');
      alert(`Message from .NET: "${msg}"`);
    }

    static async welcomeVisitor() {
      const msg = 
        await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetWelcomeMessage');
      alert(`Message from .NET: "${msg}"`);
    }
  }

  window.GreetingHelpers = GreetingHelpers;
</script>

Nell'esempio precedente:

  • La GreetingHelpers classe viene aggiunta all'oggetto window per definire globalmente la classe , che consente Blazor di individuare la classe per JS l'interoperabilità.
  • Il nome dotNetHelper della variabile è arbitrario e può essere modificato in qualsiasi nome preferito.

Nota

Per indicazioni generali sulla JS posizione e i suggerimenti per le app di produzione, vedere Posizione JavaScript nelle app ASP.NET CoreBlazor.

Chiamare i metodi di classe generica .NET

Le funzioni JavaScript (JS) possono chiamare metodi di classe generica .NET, in cui una JS funzione chiama un metodo .NET di una classe generica.

Nella classe di tipo generico seguente (GenericType<TValue>):

  • La classe ha un singolo parametro di tipo (TValue) con una singola proprietà generica Value .
  • La classe ha due metodi non generici contrassegnati con l'attributo [JSInvokable] , ognuno con un parametro di tipo generico denominato newValue:
    • Update Aggiorna in modo sincrono il valore di Value da newValue.
    • UpdateAsyncaggiorna in modo asincrono il valore di da newValue dopo la creazione di Value un'attività awaitable con Task.Yield che restituisce in modo asincrono il contesto corrente quando è atteso.
  • Ognuno dei metodi della classe scrive il tipo di TValue e il valore di Value nella console. La scrittura nella console è solo a scopo dimostrativo. Le app di produzione in genere evitano di scrivere nella console a favore della registrazione delle app. Per altre informazioni, vedere registrazione e registrazione di ASP.NET Core Blazor in .NET Core e ASP.NET Core.

Nota

I tipi e i metodi generici aperti non specificano tipi per i segnaposto dei tipi. Al contrario, i generics chiusi forniscono tipi per tutti i segnaposto di tipo. Gli esempi in questa sezione illustrano generics chiusi, ma è supportato richiamare JS i metodi di istanza di interoperabilità con generics aperti. L'uso di generics aperti non è supportato per le chiamate al metodo .NET statico, descritte in precedenza in questo articolo.

Per altre informazioni, vedere gli articoli seguenti:

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}");
    }
}

Nella funzione seguente invokeMethodsAsync :

  • I metodi e UpdateAsync della Update classe di tipo generico vengono chiamati con argomenti che rappresentano stringhe e numeri.
  • I componenti lato client supportano la chiamata sincrona dei metodi .NET con invokeMethod. syncInterop riceve un valore booleano che indica se l'interoperabilità JS si verifica nel client. Quando syncInterop è true, invokeMethod viene chiamato in modo sicuro. Se il valore di syncInterop è false, viene chiamata solo la funzione invokeMethodAsync asincrona perché l'interoperabilità JS viene eseguita in un componente lato server.
  • A scopo dimostrativo, la DotNetObjectReference chiamata di funzione (invokeMethod o invokeMethodAsync), il metodo .NET denominato (Update o UpdateAsync) e l'argomento vengono scritti nella console. Gli argomenti usano un numero casuale per consentire la corrispondenza della JS chiamata di funzione alla chiamata al metodo .NET (scritto anche nella console sul lato .NET). Il codice di produzione in genere non scrive nella console, nel client o nel server. Le app di produzione si basano in genere sulla registrazione delle app. Per altre informazioni, vedere registrazione e registrazione di ASP.NET Core Blazor in .NET Core e ASP.NET Core.
<script>
  const randomInt = () => Math.floor(Math.random() * 99999);

  window.invokeMethodsAsync = async (syncInterop, dotNetHelper1, dotNetHelper2) => {
    var n = randomInt();
    console.log(`JS: invokeMethodAsync:Update('string ${n}')`);
    await dotNetHelper1.invokeMethodAsync('Update', `string ${n}`);

    n = randomInt();
    console.log(`JS: invokeMethodAsync:UpdateAsync('string ${n}')`);
    await dotNetHelper1.invokeMethodAsync('UpdateAsync', `string ${n}`);

    if (syncInterop) {
      n = randomInt();
      console.log(`JS: invokeMethod:Update('string ${n}')`);
      dotNetHelper1.invokeMethod('Update', `string ${n}`);
    }

    n = randomInt();
    console.log(`JS: invokeMethodAsync:Update(${n})`);
    await dotNetHelper2.invokeMethodAsync('Update', n);

    n = randomInt();
    console.log(`JS: invokeMethodAsync:UpdateAsync(${n})`);
    await dotNetHelper2.invokeMethodAsync('UpdateAsync', n);

    if (syncInterop) {
      n = randomInt();
      console.log(`JS: invokeMethod:Update(${n})`);
      dotNetHelper2.invokeMethod('Update', n);
    }
  };
</script>

Nota

Per indicazioni generali sulla JS posizione e i suggerimenti per le app di produzione, vedere Posizione JavaScript nelle app ASP.NET CoreBlazor.

Nel componente GenericsExample seguente:

  • La JS funzione invokeMethodsAsync viene chiamata quando il Invoke Interop pulsante è selezionato.
  • Vengono creati e passati alla JS funzione una coppia di DotNetObjectReference tipi per le istanze di GenericType come 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();
    }
}

Nell'esempio precedente è JS un'istanza inserita IJSRuntime . IJSRuntime viene registrato dal Blazor framework.

Di seguito viene illustrato l'output tipico dell'esempio precedente quando il Invoke Interop pulsante viene selezionato in un componente lato client:

JS: invokeMethodAsync:Update('string 37802')
.NET: Update: GenericType<System.String>: string 37802
JS: invokeMethodAsync:UpdateAsync('string 53051')
JS: invokeMethod:Update('string 26784')
.NET: Update: GenericType<System.String>: string 26784
JS: invokeMethodAsync:Update(14107)
.NET: Update: GenericType<System.Int32>: 14107
JS: invokeMethodAsync:UpdateAsync(48995)
JS: invokeMethod:Update(12872)
.NET: Update: GenericType<System.Int32>: 12872
.NET: UpdateAsync: GenericType<System.String>: string 53051
.NET: UpdateAsync: GenericType<System.Int32>: 48995

Se l'esempio precedente viene implementato in un componente lato server, le chiamate sincrone con invokeMethod vengono evitate. Per i componenti lato server, è consigliabile usare la funzione asincrona (invokeMethodAsync) sulla versione sincrona (invokeMethod).

Output tipico di un componente lato server:

JS: invokeMethodAsync:Update('string 34809')
.NET: Update: GenericType<System.String>: string 34809
JS: invokeMethodAsync:UpdateAsync('string 93059')
JS: invokeMethodAsync:Update(41997)
.NET: Update: GenericType<System.Int32>: 41997
JS: invokeMethodAsync:UpdateAsync(24652)
.NET: UpdateAsync: GenericType<System.String>: stringa 93059
.NET: UpdateAsync: GenericType<System.Int32>: 24652

Gli esempi di output precedenti illustrano che i metodi asincroni vengono eseguiti e completati in un ordine arbitrario, a seconda di diversi fattori, tra cui la pianificazione dei thread e la velocità di esecuzione del metodo. Non è possibile stimare in modo affidabile l'ordine di completamento per le chiamate asincrone ai metodi.

Esempi di istanze di classe

La funzione seguente sayHello1JS :

  • Chiama il GetHelloMessage metodo .NET sull'oggetto passato DotNetObjectReference.
  • Restituisce il messaggio da GetHelloMessage al sayHello1 chiamante.
<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

Nota

Per indicazioni generali sulla JS posizione e i suggerimenti per le app di produzione, vedere Posizione JavaScript nelle app ASP.NET CoreBlazor.

Nell'esempio precedente il nome dotNetHelper della variabile è arbitrario e può essere modificato in qualsiasi nome preferito.

La classe seguente HelloHelper include un JSmetodo .NET richiamabile denominato GetHelloMessage. Quando HelloHelper viene creato, il nome nella Name proprietà viene usato per restituire un messaggio da 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}!";
}

Il CallHelloHelperGetHelloMessage metodo nella classe seguente JsInteropClasses3 richiama la JS funzione sayHello1 con una nuova istanza di 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);
    }
}

Per evitare una perdita di memoria e consentire l'operazione di Garbage Collection, il riferimento all'oggetto .NET creato da DotNetObjectReference viene eliminato quando il riferimento all'oggetto esce dall'ambito con using var la sintassi.

Quando il Trigger .NET instance method pulsante viene selezionato nel componente seguente, JsInteropClasses3.CallHelloHelperGetHelloMessage viene chiamato con il valore di 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);
    }
}

L'immagine seguente mostra il componente di cui è stato eseguito il rendering con il nome Amy Pond nel Name campo . Dopo aver selezionato il pulsante, Hello, Amy Pond! viene visualizzato nell'interfaccia utente:

Esempio di componente 'CallDotNetExample4' sottoposto a rendering

Anche il JsInteropClasses3 modello precedente illustrato nella classe può essere implementato interamente in un componente.

CallDotnet5.razor:

@page "/call-dotnet-5"
@inject IJSRuntime JS

<PageTitle>Call .NET 5</PageTitle>

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;

    public async Task TriggerDotNetInstanceMethod()
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }
}

CallDotnet5.razor:

@page "/call-dotnet-5"
@inject IJSRuntime JS

<PageTitle>Call .NET 5</PageTitle>

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;

    public async Task TriggerDotNetInstanceMethod()
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }
}

CallDotNetExample5.razor:

@page "/call-dotnet-example-5"
@inject IJSRuntime JS

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;

    public async Task TriggerDotNetInstanceMethod()
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }
}

CallDotNetExample5.razor:

@page "/call-dotnet-example-5"
@inject IJSRuntime JS

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;

    public async Task TriggerDotNetInstanceMethod()
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }
}

CallDotNetExample5.razor:

@page "/call-dotnet-example-5"
@inject IJSRuntime JS

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;

    public async Task TriggerDotNetInstanceMethod()
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }
}

CallDotNetExample5.razor:

@page "/call-dotnet-example-5"
@inject IJSRuntime JS

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;

    public async Task TriggerDotNetInstanceMethod()
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }
}

Per evitare una perdita di memoria e consentire l'operazione di Garbage Collection, il riferimento all'oggetto .NET creato da DotNetObjectReference viene eliminato quando il riferimento all'oggetto esce dall'ambito con using var la sintassi.

L'output visualizzato dal componente è Hello, Amy Pond! quando il nome Amy Pond viene specificato nel name campo .

Nel componente precedente il riferimento all'oggetto .NET viene eliminato. Se una classe o un componente non elimina DotNetObjectReference, eliminarlo dal client chiamando dispose sull'oggetto passato DotNetObjectReference:

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

Nell'esempio precedente:

  • Il {JS FUNCTION NAME} segnaposto è il JS nome della funzione.
  • Il nome dotNetHelper della variabile è arbitrario e può essere modificato in qualsiasi nome preferito.
  • Il {.NET METHOD ID} segnaposto è l'identificatore del metodo .NET.

Classe helper del metodo .NET dell'istanza del componente

Una classe helper può richiamare un metodo di istanza .NET come Action. Le classi helper sono utili negli scenari in cui l'uso di metodi .NET statici non è applicabile:

  • Quando viene eseguito il rendering di più componenti dello stesso tipo nella stessa pagina.
  • Nelle app lato server con più utenti contemporaneamente usando lo stesso componente.

Nell'esempio seguente :

  • Il componente contiene diversi ListItem1 componenti.
  • Ogni ListItem1 componente è costituito da un messaggio e da un pulsante.
  • Quando viene selezionato un ListItem1 pulsante componente, il metodo di UpdateMessage tale ListItem1metodo modifica il testo dell'elemento di elenco e nasconde il pulsante.

La classe seguente MessageUpdateInvokeHelper gestisce un JSmetodo .NET richiamabile, UpdateMessageCaller, per richiamare l'oggetto specificato quando viene creata un'istanza Action della classe .

MessageUpdateInvokeHelper.cs:

using Microsoft.JSInterop;

namespace BlazorSample;

public class MessageUpdateInvokeHelper(Action action)
{
    private readonly Action action = action;

    [JSInvokable]
    public void UpdateMessageCaller() => action.Invoke();
}
using Microsoft.JSInterop;

namespace BlazorSample;

public class MessageUpdateInvokeHelper(Action action)
{
    private readonly Action action = action;

    [JSInvokable]
    public void UpdateMessageCaller() => action.Invoke();
}
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    private Action action;

    public MessageUpdateInvokeHelper(Action action)
    {
        this.action = action;
    }

    [JSInvokable]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    private Action action;

    public MessageUpdateInvokeHelper(Action action)
    {
        this.action = action;
    }

    [JSInvokable]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}
using System;
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    private Action action;

    public MessageUpdateInvokeHelper(Action action)
    {
        this.action = action;
    }

    [JSInvokable]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}
using System;
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    private Action action;

    public MessageUpdateInvokeHelper(Action action)
    {
        this.action = action;
    }

    [JSInvokable]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}

La funzione seguente updateMessageCallerJS richiama il UpdateMessageCaller metodo .NET.

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

Nota

Per indicazioni generali sulla JS posizione e i suggerimenti per le app di produzione, vedere Posizione JavaScript nelle app ASP.NET CoreBlazor.

Nell'esempio precedente il nome dotNetHelper della variabile è arbitrario e può essere modificato in qualsiasi nome preferito.

Il componente seguente ListItem1 è un componente condiviso che può essere usato un numero qualsiasi di volte in un componente padre e crea voci di elenco (<li>...</li>) per un elenco HTML (<ul>...</ul> o <ol>...</ol>). Ogni ListItem1 istanza del componente stabilisce un'istanza di MessageUpdateInvokeHelper con un Action oggetto impostato sul relativo UpdateMessage metodo.

Quando si seleziona il pulsante di un ListItem1 componente, updateMessageCaller viene richiamato con un creato DotNetObjectReference per l'istanzaMessageUpdateInvokeHelper.InteropCall In questo modo il framework può chiamare UpdateMessageCaller l'istanza di tale ListItem1MessageUpdateInvokeHelper istanza. L'oggetto passato DotNetObjectReference viene eliminato in 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 viene chiamato per aggiornare l'interfaccia utente quando message è impostato in UpdateMessage. Se StateHasChanged non viene chiamato, Blazor non è possibile sapere che l'interfaccia utente deve essere aggiornata quando Action viene richiamato .

Il componente padre seguente include quattro voci di elenco, ognuna delle quali è un'istanza del ListItem1 componente.

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>

L'immagine seguente mostra il componente padre di cui è stato eseguito il rendering dopo aver selezionato il secondo InteropCall pulsante:

  • Il secondo ListItem1 componente ha visualizzato il UpdateMessage Called! messaggio.
  • Il InteropCall pulsante per il secondo ListItem1 componente non è visibile perché la proprietà CSS display del pulsante è impostata su none.

Esempio di componente 'CallDotNetExample6' sottoposto a rendering

Metodo .NET dell'istanza del componente chiamato da DotNetObjectReference assegnato a una proprietà dell'elemento

L'assegnazione di un DotNetObjectReference oggetto a una proprietà di un elemento HTML consente di chiamare metodi .NET in un'istanza del componente:

Analogamente all'approccio descritto nella sezione Classe helper del metodo .NET dell'istanza del componente, questo approccio è utile negli scenari in cui l'uso di metodi .NET statici non è applicabile:

  • Quando viene eseguito il rendering di più componenti dello stesso tipo nella stessa pagina.
  • Nelle app lato server con più utenti contemporaneamente usando lo stesso componente.
  • Il metodo .NET viene richiamato da un JS evento (ad esempio, onclick), non da un Blazor evento (ad esempio, @onclick).

Nell'esempio seguente :

  • Il componente contiene diversi ListItem2 componenti, ovvero un componente condiviso.
  • Ogni ListItem2 componente è costituito da un messaggio <span> di elemento di elenco e un secondo <span> con una display proprietà CSS impostata su inline-block per la visualizzazione.
  • Quando viene selezionato un ListItem2 elemento di elenco dei componenti, tale UpdateMessage ListItem2metodo modifica il testo dell'elemento di elenco nel primo <span> e nasconde il secondo <span> impostando la relativa display proprietà su none.

La funzione seguente assignDotNetHelperJS assegna a DotNetObjectReference un elemento in una proprietà denominata dotNetHelper. La funzione seguente interopCallJS usa per DotNetObjectReference l'elemento passato per richiamare un metodo .NET denominato UpdateMessage.

ListItem2.razor.js:

export function assignDotNetHelper(element, dotNetHelper) {
  element.dotNetHelper = dotNetHelper;
}

export async function interopCall(element) {
  await element.dotNetHelper.invokeMethodAsync('UpdateMessage');
}

ListItem2.razor.js:

export function assignDotNetHelper(element, dotNetHelper) {
  element.dotNetHelper = dotNetHelper;
}

export async function interopCall(element) {
  await element.dotNetHelper.invokeMethodAsync('UpdateMessage');
}
<script>
  window.assignDotNetHelper = (element, dotNetHelper) => {
    element.dotNetHelper = dotNetHelper;
  }

  window.interopCall = async (element) => {
    await element.dotNetHelper.invokeMethodAsync('UpdateMessage');
  }
</script>

Nota

Per indicazioni generali sulla JS posizione e i suggerimenti per le app di produzione, vedere Posizione JavaScript nelle app ASP.NET CoreBlazor.

Nell'esempio precedente il nome dotNetHelper della variabile è arbitrario e può essere modificato in qualsiasi nome preferito.

Il componente seguente ListItem2 è un componente condiviso che può essere usato un numero qualsiasi di volte in un componente padre e crea voci di elenco (<li>...</li>) per un elenco HTML (<ul>...</ul> o <ol>...</ol>).

Ogni ListItem2 istanza del componente richiama la assignDotNetHelperJS funzione in OnAfterRenderAsync con un riferimento all'elemento (il primo <span> elemento dell'elemento di elenco) e l'istanza del componente come .DotNetObjectReference

Quando viene selezionato il messaggio di un ListItem2 componente, interopCall viene richiamato passando l'elemento <span> come parametro (this), che richiama il UpdateMessage metodo .<span>NET. StateHasChanged In UpdateMessageviene chiamato per aggiornare l'interfaccia utente quando message è impostato e la display proprietà del secondo <span> viene aggiornata. Se StateHasChanged non viene chiamato, Blazor non è possibile sapere che l'interfaccia utente deve essere aggiornata quando viene richiamato il metodo.

L'oggetto DotNetObjectReference viene eliminato quando il componente viene eliminato.

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();
}

Il componente padre seguente include quattro voci di elenco, ognuna delle quali è un'istanza del ListItem2 componente.

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>

Interoperabilità sincrona JS nei componenti lato client

Questa sezione si applica solo ai componenti lato client.

JS le chiamate di interoperabilità sono asincrone, indipendentemente dal fatto che il codice chiamato sia sincrono o asincrono. Le chiamate sono asincrone per garantire che i componenti siano compatibili tra le modalità di rendering lato server e lato client. Nel server tutte le JS chiamate di interoperabilità devono essere asincrone perché vengono inviate tramite una connessione di rete.

Se si è certi che il componente viene eseguito solo in WebAssembly, è possibile scegliere di effettuare chiamate di interoperabilità sincrone JS . Questo comporta un sovraccarico leggermente inferiore rispetto all'esecuzione di chiamate asincrone e può comportare un minor numero di cicli di rendering perché non esiste uno stato intermedio in attesa dei risultati.

Per eseguire una chiamata sincrona da JavaScript a .NET in un componente lato client, usare DotNet.invokeMethod anziché DotNet.invokeMethodAsync.

Le chiamate sincrone funzionano se:

  • Il rendering del componente viene eseguito solo per l'esecuzione in WebAssembly.
  • La funzione chiamata restituisce un valore in modo sincrono. La funzione non è un async metodo e non restituisce .NET Task o JavaScript Promise.

Percorso JavaScript

Caricare il codice JavaScript (JS) usando uno degli approcci descritti dall'articolo sul percorso JavaScript:

L'uso dei JS moduli da caricare JS è descritto in questo articolo nella sezione Isolamento JavaScript nei moduli JavaScript.

Avviso

Inserire un <script> tag in un file di componente (.razor) solo se il componente deve adottare il rendering statico lato server (SSR statico) perché il <script> tag non può essere aggiornato in modo dinamico.

Avviso

Non inserire un <script> tag in un file di componente (.razor) perché il <script> tag non può essere aggiornato in modo dinamico.

Isolamento di JavaScript nei moduli di JavaScript

Blazor consente l'isolamento di JavaScript (JS) nei moduli JavaScript standard (specifica ECMAScript). Il caricamento del modulo JavaScript funziona allo stesso modo Blazor in quanto funziona per altri tipi di app Web e puoi personalizzare il modo in cui i moduli vengono definiti nell'app. Per una guida su come usare i moduli JavaScript, vedere Documentazione Web MDN: moduli JavaScript.

L'isolamento di JS offre i vantaggi seguenti:

  • Il codice JS importato non inquina più lo spazio dei nomi globale.
  • I consumer di una libreria e dei componenti non devono importare il codice JS correlato.

Per altre informazioni, vedere Chiamare funzioni JavaScript da metodi .NET in ASP.NET Core Blazor.

L'importazione dinamica con l'operatore import() è supportata con ASP.NET Core e Blazor:

if ({CONDITION}) import("/additionalModule.js");

Nell'esempio precedente il {CONDITION} segnaposto rappresenta un controllo condizionale per determinare se il modulo deve essere caricato.

Per la compatibilità del browser, vedere È possibile usare i moduli JavaScript: importazione dinamica.

Evitare riferimenti a oggetti circolari

Gli oggetti che contengono riferimenti circolari non possono essere serializzati nel client per:

  • Chiamate al metodo .NET.
  • Chiamate al metodo JavaScript da C# quando il tipo restituito ha riferimenti circolari.

Supporto della matrice di byte

Blazor supporta l'interoperabilità JavaScript (JS) della matrice di byte ottimizzata che evita la codifica/decodifica delle matrici di byte in Base64. Nell'esempio seguente viene JS usata l'interoperabilità per passare una matrice di byte a .NET.

Specificare una sendByteArrayJS funzione. La funzione viene chiamata in modo statico, che include il parametro del nome dell'assembly nella invokeMethodAsync chiamata, da un pulsante nel componente e non restituisce un valore:

CallDotnet8.razor.js:

export function sendByteArray() {
  const data = new Uint8Array([0x45, 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69,
    0x6e, 0x67, 0x27, 0x73, 0x20, 0x73, 0x68, 0x69, 0x6e, 0x79, 0x2c,
    0x20, 0x43, 0x61, 0x70, 0x74, 0x61, 0x69, 0x6e, 0x2e, 0x20, 0x4e,
    0x6f, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x72, 0x65, 0x74, 0x2e]);
  DotNet.invokeMethodAsync('BlazorSample', 'ReceiveByteArray', data)
    .then(str => {
      alert(str);
    });
}

export function addHandlers() {
  const btn = document.getElementById("btn");
  btn.addEventListener("click", sendByteArray);
}
<script>
  window.sendByteArray = () => {
    const data = new Uint8Array([0x45,0x76,0x65,0x72,0x79,0x74,0x68,0x69,
      0x6e,0x67,0x27,0x73,0x20,0x73,0x68,0x69,0x6e,0x79,0x2c,
      0x20,0x43,0x61,0x70,0x74,0x61,0x69,0x6e,0x2e,0x20,0x4e,
      0x6f,0x74,0x20,0x74,0x6f,0x20,0x66,0x72,0x65,0x74,0x2e]);
    DotNet.invokeMethodAsync('BlazorSample', 'ReceiveByteArray', data)
      .then(str => {
        alert(str);
      });
  };
</script>

Nota

Per indicazioni generali sulla JS posizione e i suggerimenti per le app di produzione, vedere Posizione JavaScript nelle app ASP.NET CoreBlazor.

CallDotnet8.razor:

@page "/call-dotnet-8"
@using System.Text
@implements IAsyncDisposable
@inject IJSRuntime JS

<PageTitle>Call .NET 8</PageTitle>

<h1>Call .NET Example 8</h1>

<p>
    <button id="btn">Send Bytes</button>
</p>

<p>
    Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
    <a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
    <a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>

@code {
    private IJSObjectReference? module;

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

            await module.InvokeVoidAsync("addHandlers");
        }
    }

    [JSInvokable]
    public static Task<string> ReceiveByteArray(byte[] receivedBytes) => 
        Task.FromResult(Encoding.UTF8.GetString(receivedBytes, 0, 
            receivedBytes.Length));

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            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));
    }
}

Per informazioni sull'uso di una matrice di byte quando si chiama JavaScript da .NET, vedere Chiamare le funzioni JavaScript dai metodi .NET in ASP.NET Core Blazor.

Trasmettere da JavaScript a .NET

Blazor supporta lo streaming di dati direttamente da JavaScript a .NET. I flussi vengono richiesti tramite l'interfaccia Microsoft.JSInterop.IJSStreamReference .

Microsoft.JSInterop.IJSStreamReference.OpenReadStreamAsync restituisce un Stream oggetto e utilizza i parametri seguenti:

  • maxAllowedSize: numero massimo di byte consentiti per l'operazione di lettura da JavaScript, che per impostazione predefinita è 512.000 byte se non specificato.
  • cancellationToken: oggetto CancellationToken per annullare la lettura.

In JavaScript:

function streamToDotNet() {
  return new Uint8Array(10000000);
}

Nel codice 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);

Nell'esempio precedente:

  • JS è un'istanza inserita IJSRuntime . IJSRuntime viene registrato dal Blazor framework.
  • dataReferenceStream Viene scritto su disco (file.txt) nel percorso temporaneo dell'utente corrente (GetTempPath).

Chiamare le funzioni JavaScript dai metodi .NET in ASP.NET Core Blazor illustra l'operazione inversa, il flusso da .NET a JavaScript usando .DotNetStreamReference

ASP.NET caricamenti di file di base Blazor illustra come caricare un file in Blazor. Per un esempio di moduli che trasmette i <textarea> dati in un componente lato server, vedere Risolvere i problemi relativi ai moduli ASP.NET CoreBlazor.

Interoperabilità JavaScript [JSImport]/[JSExport]

Questa sezione si applica ai componenti lato client.

In alternativa all'interazione con JavaScript (JS) nei componenti lato client usando Blazoril meccanismo di JS interoperabilità basato sull'interfaccia IJSRuntime , un'API JS[JSImport]/[JSExport] di interoperabilità è disponibile per le app destinate a .NET 7 o versione successiva.

Per altre informazioni, vedere Interoperabilità JSImport/JSExport JavaScript con ASP.NET Core Blazor.

Eliminazione dei riferimenti agli oggetti di interoperabilità JavaScript

Esempi in tutti gli articoli di interoperabilità JavaScript (JS) illustrano i modelli di eliminazione di oggetti tipici:

JS I riferimenti agli oggetti di interoperabilità vengono implementati come mappa con chiave da un identificatore sul lato della JS chiamata di interoperabilità che crea il riferimento. Quando l'eliminazione di oggetti viene avviata da .NET o JS lato, Blazor rimuove la voce dalla mappa e l'oggetto può essere sottoposto a Garbage Collection purché non sia presente alcun altro riferimento sicuro all'oggetto.

Eliminare sempre gli oggetti creati sul lato .NET per evitare perdite di memoria gestita .NET.

Attività di pulizia DOM durante l'eliminazione dei componenti

Per altre informazioni, vedere ASP.NET Core JavaScript interoperabilità (interoperabilità).For more information, see ASP.NET Core Blazor JavaScript interoperability (JS interop).

Chiamate di interoperabilità JavaScript senza circuito

Per altre informazioni, vedere ASP.NET Core JavaScript interoperabilità (interoperabilità).For more information, see ASP.NET Core Blazor JavaScript interoperability (JS interop).

Risorse aggiuntive