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

Note

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

Warning

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 10 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.

Note

Sono supportati i tipi di unione C#.

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 {PACKAGE ID/ASSEMBLY NAME} segnaposto è l'ID pacchetto del progetto (<PackageId> nel file di progetto) per una libreria o un nome di assembly per un'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('{PACKAGE ID/ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});

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

Important

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}()
    {
        ...
    }
}

Note

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 HTML onclick dell'elemento <button> corrisponde all'assegnazione del gestore di eventi onclick di JavaScript per l'elaborazione degli eventi click, non all'attributo di direttiva @onclick di Blazor. 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>

Note

Per indicazioni generali sulla posizione di JS e sulle nostre raccomandazioni per le app di produzione, vedere Posizione di JavaScript nelle app di ASP.NET Core Blazor.

Quando si seleziona il pulsante Trigger .NET static method, la console degli strumenti per sviluppatori del browser mostra i dati dell'array. 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 funzione invokeMethodAsync, passando i dati come argomenti.

Per dimostrare il passaggio di dati a .NET, passare una posizione iniziale al metodo ReturnArrayAsync nel punto in cui il metodo viene invocato 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)

Note

L'esempio di metodo ReturnArrayAsync in questa sezione restituisce il risultato di un Task senza usare in modo esplicito le parole chiave C# async e await. 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 creare un riferimento a un oggetto JS in modo che possa essere passato a .NET, dove jsObject è il 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('{PACKAGE ID/ASSEMBLY NAME}', 'ReceiveWindowObject', 
  DotNet.createJSObjectReference(window));
[JSInvokable]
public static void ReceiveWindowObject(IJSObjectReference objRef)
{
    ...
}

Nell'esempio precedente il {PACKAGE ID/ASSEMBLY NAME} segnaposto è l'ID del pacchetto del progetto (<PackageId> nel file di progetto) per una libreria o un nome di assembly per un'app.

Note

L'esempio precedente non richiede l'eliminazione di JSObjectReference, poiché in JS non viene mantenuto alcun riferimento all'oggetto window.

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

var jsObjectReference = DotNet.createJSObjectReference(window);

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

DotNet.disposeJSObjectReference(jsObjectReference);

Nell'esempio precedente il {PACKAGE ID/ASSEMBLY NAME} segnaposto è l'ID del pacchetto del progetto (<PackageId> nel file di progetto) per una libreria o un nome di assembly per un'app.

Chiama DotNet.createJSStreamReference(streamReference) per creare un riferimento di flusso JS in modo che possa essere passato a .NET, dove streamReference è un ArrayBufferarray, Blobo qualsiasi array tipizzato, ad esempio Uint8Array o Float32Array, usato per creare il riferimento al flusso JS.

Richiamare un metodo .NET di istanza

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

  • Passa l'istanza .NET per riferimento a JS racchiudendo l'istanza in un oggetto DotNetObjectReference e chiamando Create su di esso.

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

    Nell'esempio seguente :

    • dotNetHelper è un 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});
    

    Note

    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.

    Important

    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 la rimozione dei metodi .NET invocabili da JavaScript

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 l'ottimizzazione tramite trimming: DynamicDependency.

Passa un DotNetObjectReference a una singola funzione JavaScript

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

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

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

Note

Per indicazioni generali sulla posizione di JS e sulle nostre raccomandazioni per le app di produzione, vedere Posizione di JavaScript nelle app di ASP.NET Core Blazor.

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 la garbage collection, il riferimento all'oggetto .NET creato da DotNetObjectReference viene rilasciato nel metodo Dispose.

CallDotnet2.razor:

@page "/call-dotnet-2"
@implements IDisposable
@inject IJSRuntime JS

<PageTitle>Call .NET 2</PageTitle>

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

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

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

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private DotNetObjectReference<CallDotnet2>? objRef;

    protected override void OnInitialized() =>
        objRef = DotNetObjectReference.Create(this);

    public async Task TriggerDotNetInstanceMethod() =>
        result = await JS.InvokeAsync<string>("sayHello1", objRef);

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

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

CallDotnet2.razor:

@page "/call-dotnet-2"
@implements IDisposable
@inject IJSRuntime JS

<PageTitle>Call .NET 2</PageTitle>

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

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

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

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private DotNetObjectReference<CallDotnet2>? objRef;

    protected override void OnInitialized() =>
        objRef = DotNetObjectReference.Create(this);

    public async Task TriggerDotNetInstanceMethod() =>
        result = await JS.InvokeAsync<string>("sayHello1", objRef);

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

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

CallDotNetExample2.razor:

@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS

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

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

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

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private DotNetObjectReference<CallDotNetExample2>? objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

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

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

CallDotNetExample2.razor:

@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS

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

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

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

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private DotNetObjectReference<CallDotNetExample2>? objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

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

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

CallDotNetExample2.razor:

@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS

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

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

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

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private DotNetObjectReference<CallDotNetExample2> objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

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

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

CallDotNetExample2.razor:

@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS

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

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

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

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private DotNetObjectReference<CallDotNetExample2> objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

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

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

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

Usa le linee guida 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.

Passa DotNetObjectReference a 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 oggetto DotNetObjectReference dal metodo OnAfterRenderAsync del ciclo di vita a una classe JS affinché venga usato da più funzioni. Assicurarsi che il codice .NET elimini , DotNetObjectReferencecome illustrato nell'esempio seguente.

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

CallDotNetExampleOneHelper.razor:

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

<PageTitle>Call .NET Example</PageTitle>

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

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

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

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

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

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

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

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

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

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

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

        dotNetHelper?.Dispose();
    }
}

Nell'esempio precedente:

  • JS è un'istanza IJSRuntime iniettata. 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 rimane bloccato durante la rimozione del modulo nel caso in cui si perda il circuito SignalR di Blazor. Se il codice precedente viene usato in un'app Blazor WebAssembly, non c'è alcuna connessione SignalR da perdere, quindi è possibile rimuovere il blocco try-catch e lasciare la riga che rilascia il modulo (await module.DisposeAsync();). Per altre informazioni, vedere ASP.NET Core Blazor interoperabilità JavaScript (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 IJSRuntime iniettata. 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 classe GreetingHelpers viene aggiunta all'oggetto window per definire la classe a livello globale, il che consente a Blazor di individuare la classe per l'interoperabilità con JS.
  • Il nome dotNetHelper della variabile è arbitrario e può essere modificato in qualsiasi nome preferito.

Note

Per indicazioni generali sulla posizione di JS e sulle nostre raccomandazioni per le app di produzione, vedere Posizione di JavaScript nelle app di ASP.NET Core Blazor.

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.
    • UpdateAsync aggiorna in modo asincrono il valore di Value da newValue dopo aver creato, con Task.Yield, un'attività awaitable che cede nuovamente in modo asincrono il controllo al contesto corrente quando viene attesa.
  • 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 applicazioni in produzione in genere evitano di scrivere nella console, preferendo il logging dell'applicazione. Per ulteriori informazioni, vedere registrazione in ASP.NET CoreBlazor e Logging in .NET e ASP.NET Core.

Note

I tipi e i metodi generici aperti non specificano tipi per i segnaposto dei tipi. Al contrario, i tipi generici chiusi specificano tipi per tutti i segnaposto di tipo. Gli esempi in questa sezione illustrano tipi generici chiusi, ma è supportato il richiamo di JS metodi di istanza interop con tipi generici 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 Update e UpdateAsync della 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 chiamata di funzione DotNetObjectReference (invokeMethod o invokeMethodAsync), il metodo .NET chiamato (Update o UpdateAsync) e l'argomento vengono visualizzati nella console. Gli argomenti usano un numero casuale per consentire di associare la chiamata di funzione JS all’invocazione del metodo .NET (anch’esso scritto nella console sul lato .NET). Il codice di produzione di solito non scrive sulla console, né lato client né lato server. Le app di produzione si basano in genere sulla registrazione delle app. Per ulteriori informazioni, vedere registrazione in ASP.NET CoreBlazor e Logging in .NET e ASP.NET Core.
<script>
  const randomInt = () => Math.floor(Math.random() * 99999);

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

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

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

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

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

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

Note

Per indicazioni generali sulla posizione di JS e sulle nostre raccomandazioni per le app di produzione, vedere Posizione di JavaScript nelle app di ASP.NET Core Blazor.

Nel componente GenericsExample seguente:

  • La JS funzione invokeMethodsAsync viene chiamata quando il Invoke Interop pulsante è selezionato.
  • Viene creata una coppia di tipi DotNetObjectReference e passata alla funzione JS per le istanze di GenericType come un string e un 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 IJSRuntime iniettata. 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: Aggiornamento: GenericType<System.String>: stringa 37802
JS: invokeMethodAsync:UpdateAsync('string 53051')
JS: invokeMethod:Update('string 26784')
.NET: Update: GenericType<System.String>: string 26784
JS: invokeMethodAsync:Update(14107)
.NET: Aggiornamento: GenericType<System.Int32>: 14107
JS: invokeMethodAsync:UpdateAsync(48995)
JS: invokeMethod:Update(12872)
.NET: Aggiornamento: 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 sul lato server:

JS: invokeMethodAsync:Update('string 34809')
.NET: Aggiornamento: GenericType<System.String>: string 34809
JS: invokeMethodAsync:UpdateAsync('string 93059')
JS: invokeMethodAsync:Update(41997)
.NET: Aggiornamento: GenericType<System.Int32>: 41997
JS: invokeMethodAsync:UpdateAsync(24652)
.NET: UpdateAsync: GenericType<System.String>: string 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 prevedere in modo affidabile l'ordine di completamento delle chiamate di metodo asincrone.

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>

Note

Per indicazioni generali sul JS posizionamento e sui nostri suggerimenti per le app di produzione, vedi Posizionamento di JavaScript nelle app di 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 metodo CallHelloHelperGetHelloMessage nella classe JsInteropClasses3 seguente invoca la funzione sayHello1JS 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 segnaposto {JS FUNCTION NAME} è il nome della funzione JS.
  • 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 per il 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 pulsante del componente ListItem1, il metodo UpdateMessage di ListItem1 modifica il testo dell'elemento dell'elenco e nasconde il pulsante.

La classe seguente MessageUpdateInvokeHelper contiene un metodo .NET richiamabile tramite JS, UpdateMessageCaller, per richiamare l'Action specificato quando viene creata un'istanza 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>

Note

Per indicazioni generali sulla posizione di JS e sulle nostre raccomandazioni per le app di produzione, vedere Posizione di JavaScript nelle app di ASP.NET Core Blazor.

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 istanza del componente ListItem1 crea un'istanza di MessageUpdateInvokeHelper con un oggetto Action impostato sul suo metodo UpdateMessage.

Quando si seleziona il pulsante InteropCall di un componente ListItem1, viene richiamato updateMessageCaller con un DotNetObjectReference creato per l'istanza MessageUpdateInvokeHelper. Ciò consente al framework di chiamare UpdateMessageCaller sull'istanza MessageUpdateInvokeHelper di ListItem1. 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 componente ListItem1.

CallDotnet6.razor:

@page "/call-dotnet-6"

<PageTitle>Call .NET 6</PageTitle>

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

<ul>
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
</ul>

CallDotnet6.razor:

@page "/call-dotnet-6"

<PageTitle>Call .NET 6</PageTitle>

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

<ul>
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
</ul>

CallDotNetExample6.razor:

@page "/call-dotnet-example-6"

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

<ul>
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
</ul>

CallDotNetExample6.razor:

@page "/call-dotnet-example-6"

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

<ul>
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
</ul>

CallDotNetExample6.razor:

@page "/call-dotnet-example-6"

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

<ul>
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
</ul>

CallDotNetExample6.razor:

@page "/call-dotnet-example-6"

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

<ul>
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
</ul>

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 ListItem2UpdateMessagemetodo 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>

Note

Per indicazioni generali sulla posizione di JS e sulle nostre raccomandazioni per le app di produzione, vedere Posizione di JavaScript nelle app di ASP.NET Core Blazor.

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 istanza del componente ListItem2 richiama la funzione assignDotNetHelperJS in OnAfterRenderAsync con un riferimento all'elemento (il primo elemento <span> della voce dell'elenco) e l'istanza del componente come DotNetObjectReference.

Quando viene selezionato il messaggio di un componente ListItem2, viene invocato interopCall passando l'elemento <span> come parametro (this), che a sua volta richiama il metodo .NET UpdateMessage. In UpdateMessage, StateHasChanged viene chiamato per aggiornare l'interfaccia utente quando message viene impostato e la proprietà display 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.

DotNetObjectReference viene rilasciato 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 componente ListItem2.

CallDotnet7.razor:

@page "/call-dotnet-7"

<PageTitle>Call .NET 7</PageTitle>

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

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotnet7.razor:

@page "/call-dotnet-7"

<PageTitle>Call .NET 7</PageTitle>

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

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotNetExample7.razor:

@page "/call-dotnet-example-7"

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

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotNetExample7.razor:

@page "/call-dotnet-example-7"

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

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotNetExample7.razor:

@page "/call-dotnet-example-7"

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

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotNetExample7.razor:

@page "/call-dotnet-example-7"

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

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

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.

Posizione JavaScript

Carica il codice JavaScript (JS) usando uno degli approcci descritti nell'articolo sulla posizione di JavaScript:

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

Warning

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.

Warning

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.
  • Gli utenti di una libreria e dei relativi componenti non sono tenuti a importare il file 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 di metodo .NET.
  • Chiamate di metodo JavaScript da C# quando il tipo di ritorno ha riferimenti circolari.

Supporto della matrice di byte

Blazor supporta l'interoperabilità JavaScript (JS) con array di byte ottimizzati che evita la codifica e la decodifica degli array di byte in Base64. L'esempio seguente usa l'JSinterop per passare un array 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>

Note

Per indicazioni generali sulla posizione di JS e sulle nostre raccomandazioni per le app di produzione, vedere Posizione di JavaScript nelle app di ASP.NET Core Blazor.

CallDotnet8.razor:

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

<PageTitle>Call .NET 8</PageTitle>

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

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

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

@code {
    private IJSObjectReference? module;

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

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

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

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

CallDotnet8.razor:

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

<PageTitle>Call .NET 8</PageTitle>

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

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

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

@code {
    private IJSObjectReference? module;

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

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

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

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

CallDotNetExample8.razor:

@page "/call-dotnet-example-8"
@using System.Text

<PageTitle>Call .NET 8</PageTitle>

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

<p>
    <button onclick="sendByteArray()">Send Bytes</button>
</p>

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

@code {
    [JSInvokable]
    public static Task<string> ReceiveByteArray(byte[] receivedBytes)
    {
        return Task.FromResult(
            Encoding.UTF8.GetString(receivedBytes, 0, receivedBytes.Length));
    }
}

CallDotNetExample8.razor:

@page "/call-dotnet-example-8"
@using System.Text

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

<p>
    <button onclick="sendByteArray()">Send Bytes</button>
</p>

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

@code {
    [JSInvokable]
    public static Task<string> ReceiveByteArray(byte[] receivedBytes)
    {
        return Task.FromResult(
            Encoding.UTF8.GetString(receivedBytes, 0, receivedBytes.Length));
    }
}

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: Un oggetto CancellationToken per annullare l'operazione di 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 IJSRuntime iniettata. IJSRuntime viene registrato dal Blazor framework.
  • Il dataReferenceStream viene scritto su disco (file.txt) nel percorso della cartella temporanea dell'utente corrente (GetTempPath).

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

Caricamento di file in ASP.NET Core Blazor illustra come caricare un file in Blazor. Per un esempio di form che trasmette dati <textarea> in un componente lato server, vedere Risolvere i problemi dei form di ASP.NET Core Blazor.

Interop JavaScript [JSImport]/[JSExport]

Questa sezione si applica ai componenti lato client.

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

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

Eliminazione dei riferimenti agli oggetti di interoperabilità JavaScript

Esempi presenti negli articoli sull'interoperabilità di JavaScript (JS) illustrano i tipici schemi di eliminazione degli oggetti:

JS I riferimenti agli oggetti di interoperabilità vengono implementati come una mappa indicizzata tramite un identificatore dal lato della chiamata di interoperabilità JS 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.

Come minimo, rilasciare sempre gli oggetti creati dal lato .NET per evitare perdite di memoria gestita da .NET.

Attività di pulizia DOM durante l'eliminazione dei componenti

Per altre informazioni, vedere ASP.NET Core Blazor interoperabilità JavaScript (JS interop).

Chiamate di interoperabilità con JavaScript senza un circuito

Per altre informazioni, vedere ASP.NET Core Blazor interoperabilità JavaScript (JS interop).

Risorse aggiuntive