Partager via


Appeler des méthodes .NET à partir de fonctions JavaScript dans ASP.NET Core Blazor

Remarque

Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 9 de cet article.

Avertissement

Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la stratégie de support .NET et .NET Core. Pour la version actuelle, consultez la version .NET 9 de cet article.

Important

Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.

Pour la version actuelle, consultez la version .NET 9 de cet article.

Cet article explique comment appeler des méthodes .NET à partir de JavaScript (JS).

Pour plus d’informations sur l’appel de fonctions JS à partir de .NET, consultez Appeler des fonctions JavaScript à partir de méthodes .NET dans ASP.NET Core Blazor.

Appeler une méthode .NET statique

Pour appeler une méthode .NET statique à partir de JavaScript (JS), utilisez les fonctions JS :

  • DotNet.invokeMethodAsync (recommandé) : asynchrone pour les composants côté serveur et côté client.
  • DotNet.invokeMethod : synchrone uniquement pour les composants côté client uniquement.

Transmettez le nom de l’assembly contenant la méthode, l’identificateur de la méthode .NET statique et les arguments éventuels.

Dans l’exemple suivant :

  • L’espace réservé {ASSEMBLY NAME} est le nom de l’assembly de l’application.
  • L’espace réservé {.NET METHOD ID} est l’identificateur de méthode .NET.
  • L’espace réservé {ARGUMENTS} reçoit des arguments facultatifs séparés par des virgules à transmettre à la méthode, chacun d’entre eux devant être sérialisable en JSON.
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});

DotNet.invokeMethodAsync retourne un JS Promise représentant le résultat de l’opération. DotNet.invokeMethod (composants côté client) retourne le résultat de l’opération.

Important

Pour les composants côté serveur, nous recommandons la fonction asynchrone (invokeMethodAsync) plutôt que la version synchrone (invokeMethod).

La méthode .NET doit être publique, statique et avoir l’attribut [JSInvokable].

Dans l’exemple suivant :

  • L’espace réservé {<T>} indique le type de retour, qui est uniquement requis pour les méthodes qui retournent une valeur.
  • L’espace réservé {.NET METHOD ID} est l’identificateur de méthode.
@code {
    [JSInvokable]
    public static Task{<T>} {.NET METHOD ID}()
    {
        ...
    }
}

Remarque

L’appel de méthodes génériques ouvertes n’est pas pris en charge avec les méthodes .NET statiques, mais l’est avec les méthodes d’instance. Pour plus d’informations, consultez la section Appeler des méthodes de classe génériques .NET.

Dans le composant suivant, la méthode C# ReturnArrayAsync retourne un tableau int. L’attribut [JSInvokable] est appliqué à la méthode, ce qui rend la méthode disponible pour 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 fonction addHandlersJS ajoute un événement click au bouton. La fonction returnArrayAsyncJS est affectée en tant que gestionnaire.

La fonction returnArrayAsyncJS appelle la méthode .NET ReturnArrayAsync du composant, ce qui enregistre le résultat dans la console des outils de développement web du navigateur. BlazorSample est le nom de l’assembly de l’application.

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 fonction addHandlersJS ajoute un événement click au bouton. La fonction returnArrayAsyncJS est affectée en tant que gestionnaire.

La fonction returnArrayAsyncJS appelle la méthode .NET ReturnArrayAsync du composant, ce qui enregistre le résultat dans la console des outils de développement web du navigateur. BlazorSample est le nom de l’assembly de l’application.

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’attribut HTML onclick de l’élément <button> est l’attribution de gestionnaire d’événements onclick JavaScript pour le traitement des événements click, et non pas l’attribut de directive @onclick de Blazor. La fonction returnArrayAsyncJS est affectée en tant que gestionnaire.

La fonction returnArrayAsyncJS suivante appelle la méthode .NET ReturnArrayAsync du composant, ce qui enregistre le résultat dans la console des outils de développement web du navigateur. BlazorSample est le nom de l’assembly de l’application.

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

Remarque

Pour obtenir une aide générale sur l’emplacement deJS et nos recommandations pour les applications de production, consultez Emplacement de JavaScript dans les applications Blazor ASP.NET Core.

Lorsque le bouton Trigger .NET static method est sélectionné, la sortie de la console des outils de développement du navigateur affiche les données du tableau. Le format de la sortie diffère légèrement d’un navigateur à l’autre. La sortie suivante montre le format utilisé par Microsoft Edge :

Array(3) [ 11, 12, 13 ]

Transmettez des données à une méthode .NET lors de l’appel de la fonction invokeMethodAsync en passant les données en tant qu’arguments.

Pour illustrer le passage de données à .NET, passez une position de départ à la méthode ReturnArrayAsync dans laquelle la méthode est appelée dans 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>

La méthode ReturnArrayAsync disponible du composant reçoit la position de départ et construit le tableau à partir de celle-ci. Le tableau est retourné pour la journalisation dans la console :

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

Une fois l’application recompilée et le navigateur actualisé, la sortie suivante s’affiche dans la console du navigateur lorsque le bouton est sélectionné :

Array(3) [ 14, 15, 16 ]

L’identificateur de méthode .NET pour l’appel JS est le nom de la méthode .NET, mais vous pouvez spécifier un identificateur différent à l’aide du constructeur d’attribut [JSInvokable]. Dans l’exemple suivant, DifferentMethodName est l’identificateur de méthode attribué pour la méthode ReturnArrayAsync :

[JSInvokable("DifferentMethodName")]

Dans l’appel à DotNet.invokeMethodAsync (composants côté serveur ou côté client) ou DotNet.invokeMethod (composants côté client uniquement), appelez DifferentMethodName pour exécuter la méthode .NET ReturnArrayAsync :

  • DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
  • DotNet.invokeMethod('BlazorSample', 'DifferentMethodName'); (composants côté client uniquement)

Remarque

L’exemple de méthode ReturnArrayAsync de cette section retourne le résultat d’un Task sans utiliser de mots clés C# async et await explicites. Le codage de méthodes avec async et await est typique des méthodes qui utilisent le mot clé await pour retourner la valeur des opérations asynchrones.

Méthode composée ReturnArrayAsync avec des mots clés async et await :

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

Pour plus d’informations, consultez Programmation asynchrone avec async et await dans le guide C#.

Créer des références d’objet et de données JavaScript à passer à .NET

Appelez DotNet.createJSObjectReference(jsObject) pour construire une référence d’objet JS afin qu’elle puisse être transmise à .NET, où jsObject est le JS Object utilisé pour créer la référence d’objet JS. L’exemple suivant transmet une référence à l’objet window non sérialisable à .NET, qui le reçoit dans la méthode C# ReceiveWindowObject en tant que IJSObjectReference :

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

Dans l’exemple précédent, l’espace réservé {ASSEMBLY NAME} est l’espace de noms de l’application.

Remarque

L’exemple précédent ne nécessite pas de suppression du JSObjectReference, car aucune référence à l’objet window n’est conservée dans JS.

La conservation d’une référence à un JSObjectReference nécessite la suppression de celle-ci pour éviter les fuites de mémoire JS sur le client. L’exemple suivant refactorise le code précédent pour capturer une référence au JSObjectReference, suivi d’un appel à DotNet.disposeJSObjectReference() pour supprimer la référence :

var jsObjectReference = DotNet.createJSObjectReference(window);

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

DotNet.disposeJSObjectReference(jsObjectReference);

Dans l’exemple précédent, l’espace réservé {ASSEMBLY NAME} est l’espace de noms de l’application.

Appelez DotNet.createJSStreamReference(streamReference) pour construire une référence de flux JS afin qu’elle puisse être transmise à .NET, où streamReference est un ArrayBuffer, Blob ou tout tableau typé, comme Uint8Array ou Float32Array, utilisé pour créer la référence de flux JS.

Appeler une méthode .NET d’instance

Pour appeler une méthode .NET d’instance à partir de JavaScript (JS) :

  • Transmettez l’instance .NET par référence à JS en encapsulant l’instance dans un DotNetObjectReference et en appelant Create dessus.

  • Appelez une méthode d’instance .NET à partir de JS en utilisant invokeMethodAsync (recommandé) ou invokeMethod (composants côté client uniquement) à partir de l’instance DotNetObjectReference passée. Transmettez l’identificateur de la méthode .NET d’instance et les arguments éventuels. L’instance .NET peut également être passée en tant qu’argument lors de l’appel d’autres méthodes .NET à partir de JS.

    Dans l’exemple suivant :

    • dotNetHelper est un DotNetObjectReference.
    • L’espace réservé {.NET METHOD ID} est l’identificateur de méthode .NET.
    • L’espace réservé {ARGUMENTS} reçoit des arguments facultatifs séparés par des virgules à transmettre à la méthode, chacun d’entre eux devant être sérialisable en JSON.
    dotNetHelper.invokeMethodAsync('{.NET METHOD ID}', {ARGUMENTS});
    

    Remarque

    invokeMethodAsync et invokeMethod n’acceptent pas de paramètre de nom d’assembly lors de l’appel d’une méthode d’instance.

    invokeMethodAsync retourne un JS Promise représentant le résultat de l’opération. invokeMethod (composants côté client uniquement) retourne le résultat de l’opération.

    Important

    Pour les composants côté serveur, nous recommandons la fonction asynchrone (invokeMethodAsync) plutôt que la version synchrone (invokeMethod).

  • Supprimez le DotNetObjectReference.

Les sections suivantes de cet article illustrent différentes approches pour appeler une méthode .NET d’instance :

Évitez de découper les méthodes .NET disponibles pour JavaScript

Cette section s’applique aux applications côté client avec la compilation à l’avance (AOT) et la liaison dynamique à l’exécution activées.

Plusieurs des exemples des sections suivantes sont basés sur une approche d’instance de classe, où la méthode .NET disponible pour JavaScript marquée avec l’attribut [JSInvokable] est membre d’une classe qui n’est pas un composant Razor. Lorsque ces méthodes .NET se trouvent dans un composant Razor, elles sont protégées contre la nouvelle liaison/le découpage du runtime. Pour protéger les méthodes .NET contre le découpage en dehors des composants Razor, implémentez les méthodes avec l’attribut DynamicDependency sur le constructeur de la classe, comme le montre l’exemple suivant :

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

public class ExampleClass {

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

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

Pour plus d’informations, consultez Préparer des bibliothèques .NET pour le découpage : DynamicDependency.

Passer une DotNetObjectReference à une fonction JavaScript individuelle

L’exemple de cette section montre comment passer une DotNetObjectReference à une fonction JavaScript individuelle (JS).

La fonction sayHello1JS suivante reçoit DotNetObjectReference et appelle invokeMethodAsync pour appeler la méthode .NET GetHelloMessage d’un composant :

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

Remarque

Pour obtenir une aide générale sur l’emplacement deJS et nos recommandations pour les applications de production, consultez Emplacement de JavaScript dans les applications Blazor ASP.NET Core.

Dans l’exemple précédent, le nom de variable dotNetHelper est arbitraire et peut être remplacé par n’importe quel nom de votre choix.

Pour le composant suivant :

  • Le composant a une méthode .NET disponible pour JS nommée GetHelloMessage.
  • Lorsque le bouton Trigger .NET instance method est sélectionné, la fonction JS sayHello1 est appelée avec la DotNetObjectReference.
  • sayHello1:
    • Appelle GetHelloMessage et reçoit le résultat du message.
    • Retourne le résultat du message à la méthode TriggerDotNetInstanceMethod appelante.
  • Le message retourné à partir de sayHello1 dans result s’affiche à l’utilisateur.
  • Pour éviter une fuite de mémoire et autoriser le nettoyage de la mémoire, la référence d’objet .NET créée par DotNetObjectReference est supprimée dans la méthode 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();
    }
}

Dans l’exemple précédent, le nom de variable dotNetHelper est arbitraire et peut être remplacé par n’importe quel nom de votre choix.

Utilisez les instructions suivantes pour passer des arguments à une méthode d’instance :

Ajoutez des paramètres à l’appel de méthode .NET. Dans l’exemple suivant, un nom est passé à la méthode. Ajoutez des paramètres supplémentaires à la liste en fonction de vos besoins.

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

Dans l’exemple précédent, le nom de variable dotNetHelper est arbitraire et peut être remplacé par n’importe quel nom de votre choix.

Fournissez la liste des paramètres à la méthode .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();
    }
}

Dans l’exemple précédent, le nom de variable dotNetHelper est arbitraire et peut être remplacé par n’importe quel nom de votre choix.

Passer une DotNetObjectReference à une classe avec plusieurs fonctions JavaScript

L’exemple de cette section montre comment passer une DotNetObjectReference à une classe JavaScript (JS) avec plusieurs fonctions.

Créez et passez une DotNetObjectReference de la méthode de cycle de vie OnAfterRenderAsync à une classe JS que plusieurs fonctions utiliseront. Assurez-vous que le code .NET supprime DotNetObjectReference, comme le montre l’exemple suivant.

Dans le composant suivant, les boutons Trigger JS function appellent des fonctions JS en définissant la propriété JSonclick, et non la directive d’attribut @onclick de Blazor.

CallDotNetExampleOneHelper.razor:

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

<PageTitle>Call .NET Example</PageTitle>

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

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

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

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

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

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

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

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

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

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

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

        dotNetHelper?.Dispose();
    }
}

Dans l'exemple précédent :

  • JS est un instance IJSRuntime injectée. IJSRuntime est inscrit par le framework Blazor.
  • Le nom de variable dotNetHelper est arbitraire et peut être remplacé par n’importe quel nom de votre choix.
  • Le composant doit explicitement éliminer DotNetObjectReference pour autoriser le nettoyage de la mémoire et empêcher une fuite de mémoire.
  • JSDisconnectedException est piégé lors de l’élimination des modules au cas où Blazorle circuit est SignalR perdu. Si le code précédent est utilisé dans une Blazor WebAssembly application, il n’existe aucune SignalR connexion à perdre. Vous pouvez donc supprimer lecatch try-bloc et laisser la ligne qui supprime le module ().await module.DisposeAsync(); Pour plus d’informations, consultez Interopérabilité JavaScript et ASP.NET Core Blazor (interopérabilité JS).

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

Dans l’exemple précédent, le nom de variable dotNetHelper est arbitraire et peut être remplacé par n’importe quel nom de votre choix.

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

Dans l'exemple précédent :

  • JS est un instance IJSRuntime injectée. IJSRuntime est inscrit par le framework Blazor.
  • Le nom de variable dotNetHelper est arbitraire et peut être remplacé par n’importe quel nom de votre choix.
  • Le composant doit explicitement éliminer DotNetObjectReference pour autoriser le nettoyage de la mémoire et empêcher une fuite de mémoire.
<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>

Dans l’exemple précédent :

  • La classe GreetingHelpers est ajoutée à l’objet window pour définir globalement la classe, ce qui permet à Blazor de localiser la classe pour l’interopérabilité JS.
  • Le nom de variable dotNetHelper est arbitraire et peut être remplacé par n’importe quel nom de votre choix.

Remarque

Pour obtenir une aide générale sur l’emplacement deJS et nos recommandations pour les applications de production, consultez Emplacement de JavaScript dans les applications Blazor ASP.NET Core.

Appeler des méthodes de classe génériques .NET

Les fonctions JavaScript (JS) peuvent appeler des méthodes de classe générique .NET, où une fonction JS appelle une méthode .NET d’une classe générique.

Dans la classe de type générique suivante (GenericType<TValue>) :

  • La classe a un paramètre de type unique (TValue) avec une propriété générique Value unique.
  • La classe a deux méthodes non génériques marquées avec l’attribut [JSInvokable], chacune avec un paramètre de type générique nommé newValue :
    • Update met à jour de manière synchrone la valeur de Value à partir de newValue.
    • UpdateAsync met à jour de manière asynchrone la valeur de Value à partir de newValue après la création d’une tâche pouvant être attendue, avec Task.Yield qui retourne de façon asynchrone au contexte actuel en cas d’attente.
  • Chacune des méthodes de la classe écrit le type de TValue et la valeur de Value dans la console. L’écriture dans la console est effectuée uniquement à des fins de démonstration. Les applications de production évitent généralement d’écrire dans la console et préfèrent la journalisation de l’application. Pour plus d’informations, consultez Journalisation dans ASP.NET Core Blazor et Journalisation dans .NET Core et ASP.NET Core.

Remarque

Les types et méthodes génériques ouverts ne spécifient pas de types pour les espaces réservés de type. À l’inverse, les génériques fermés fournissent des types pour tous les espaces réservés de type. Les exemples de cette section illustrent les génériques fermés, mais l’appel de méthodes d’instance d’interopérabilité JS avec des génériques ouverts est pris en charge. L’utilisation de génériques ouverts n’est pas prise en charge pour les appels de méthode .NET statiques, décrits plus haut dans cet article.

Pour plus d’informations, consultez les articles suivants :

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

Dans la fonction invokeMethodsAsync suivante :

  • Les méthodes Update et UpdateAsync de la classe de type générique sont appelées avec des arguments représentant des chaînes et des nombres.
  • Les composants côté client prennent en charge l’appel de méthodes .NET de manière synchrone avec invokeMethod. syncInterop reçoit une valeur booléenne indiquant si l’interopérabilité JS se produit dans le client. Quand syncInterop est true, invokeMethod est appelé de façon sécurisée. Si la valeur de syncInterop est false, seule la fonction asynchrone invokeMethodAsync est appelée, car l’interopérabilité JS s’exécute dans un composant côté serveur.
  • À des fins de démonstration, l’appel de fonction DotNetObjectReference (invokeMethod ou invokeMethodAsync), la méthode .NET appelée (Update ou UpdateAsync) et l’argument sont écrits dans la console. Les arguments utilisent un nombre aléatoire pour permettre la correspondance de l’appel de fonction JS à l’appel de méthode .NET (également écrit dans la console côté .NET). Le code de production n’écrit généralement pas dans la console, ni sur le client ni sur le serveur. Les applications de production s’appuient généralement sur la journalisation de l’application. Pour plus d’informations, consultez Journalisation dans ASP.NET Core Blazor et Journalisation dans .NET Core et 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>

Remarque

Pour obtenir une aide générale sur l’emplacement deJS et nos recommandations pour les applications de production, consultez Emplacement de JavaScript dans les applications Blazor ASP.NET Core.

Dans le composant GenericsExample suivant :

  • La fonction JS invokeMethodsAsync est appelée lorsque le bouton Invoke Interop est sélectionné.
  • Une paire de types DotNetObjectReference est créée et transmise à la fonction JS pour les instances de GenericType en tant que string et 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();
    }
}

Dans l’exemple précédent, JS est une instance IJSRuntime injectée. IJSRuntime est inscrit par le framework Blazor.

L’exemple suivant illustre la sortie classique de l’exemple précédent lorsque le bouton Invoke Interop est sélectionné dans un composant côté client :

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

Si l’exemple précédent est implémenté dans un composant côté serveur, les appels synchrones avec invokeMethod sont évités. Pour les composants côté serveur, nous recommandons la fonction asynchrone (invokeMethodAsync) plutôt que la version synchrone (invokeMethod).

Sortie classique d’un composant côté serveur :

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

Les exemples de sortie précédents montrent que les méthodes asynchrones s’exécutent et se terminent dans un ordre arbitraire en fonction de plusieurs facteurs, notamment la planification des threads et la vitesse d’exécution de la méthode. Il n’est pas possible de prédire de manière fiable l’ordre d’achèvement des appels de méthode asynchrones.

Exemples d’instance de classe

La fonction sayHello1JS suivante :

  • Appelle la méthode GetHelloMessage .NET sur la DotNetObjectReference passée.
  • Retourne le message de GetHelloMessage à l’appelant sayHello1.
<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

Remarque

Pour obtenir une aide générale sur l’emplacement deJS et nos recommandations pour les applications de production, consultez Emplacement de JavaScript dans les applications Blazor ASP.NET Core.

Dans l’exemple précédent, le nom de variable dotNetHelper est arbitraire et peut être remplacé par n’importe quel nom de votre choix.

La classe HelloHelper suivante a une méthode .NET disponible pour JS nommée GetHelloMessage. Quand HelloHelper est créé, le nom de la propriété Name est utilisé pour renvoyer un message à partir de GetHelloMessage.

HelloHelper.cs:

using Microsoft.JSInterop;

namespace BlazorSample;

public class HelloHelper(string? name)
{
    public string? Name { get; set; } = name ?? "No Name";

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;

namespace BlazorSample;

public class HelloHelper(string? name)
{
    public string? Name { get; set; } = name ?? "No Name";

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;

public class HelloHelper
{
    public HelloHelper(string? name)
    {
        Name = name ?? "No Name";
    }

    public string? Name { get; set; }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;

public class HelloHelper
{
    public HelloHelper(string? name)
    {
        Name = name ?? "No Name";
    }

    public string? Name { get; set; }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;

public class HelloHelper
{
    public HelloHelper(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;

public class HelloHelper
{
    public HelloHelper(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

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

La méthode CallHelloHelperGetHelloMessage de la classe JsInteropClasses3 suivante appelle la fonction JS sayHello1 avec une nouvelle instance de HelloHelper.

JsInteropClasses3.cs:

using Microsoft.JSInterop;

namespace BlazorSample;

public class JsInteropClasses3(IJSRuntime js)
{
    private readonly IJSRuntime js = js;

    public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        return await js.InvokeAsync<string>("sayHello1", objRef);
    }
}
using Microsoft.JSInterop;

namespace BlazorSample;

public class JsInteropClasses3(IJSRuntime js)
{
    private readonly IJSRuntime js = js;

    public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        return await js.InvokeAsync<string>("sayHello1", objRef);
    }
}
using Microsoft.JSInterop;

public class JsInteropClasses3
{
    private readonly IJSRuntime js;

    public JsInteropClasses3(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        return await js.InvokeAsync<string>("sayHello1", objRef);
    }
}
using Microsoft.JSInterop;

public class JsInteropClasses3
{
    private readonly IJSRuntime js;

    public JsInteropClasses3(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        return await js.InvokeAsync<string>("sayHello1", objRef);
    }
}
using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses3
{
    private readonly IJSRuntime js;

    public JsInteropClasses3(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask<string> CallHelloHelperGetHelloMessage(string name)
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        return await js.InvokeAsync<string>("sayHello1", objRef);
    }
}
using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses3
{
    private readonly IJSRuntime js;

    public JsInteropClasses3(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask<string> CallHelloHelperGetHelloMessage(string name)
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        return await js.InvokeAsync<string>("sayHello1", objRef);
    }
}

Pour éviter une fuite de mémoire et autoriser le nettoyage de la mémoire, la référence d’objet .NET créée par DotNetObjectReference est supprimée lorsque la référence d’objet sort de l’étendue avec la syntaxe using var.

Lorsque le bouton Trigger .NET instance method est sélectionné dans le composant suivant, JsInteropClasses3.CallHelloHelperGetHelloMessage est appelé avec la valeur de name.

CallDotnet4.razor:

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

<PageTitle>Call .NET 4</PageTitle>

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

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

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

<p>
    @result
</p>

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

    protected override void OnInitialized() => 
        jsInteropClasses = new JsInteropClasses3(JS);

    private async Task TriggerDotNetInstanceMethod()
    {
        if (jsInteropClasses is not null)
        {
            result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
        }
    }
}

CallDotnet4.razor:

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

<PageTitle>Call .NET 4</PageTitle>

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

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

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

<p>
    @result
</p>

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

    protected override void OnInitialized() => 
        jsInteropClasses = new JsInteropClasses3(JS);

    private async Task TriggerDotNetInstanceMethod()
    {
        if (jsInteropClasses is not null)
        {
            result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
        }
    }
}

CallDotNetExample4.razor:

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

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

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

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

<p>
    @result
</p>

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

    protected override void OnInitialized()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
    }

    private async Task TriggerDotNetInstanceMethod()
    {
        if (jsInteropClasses is not null)
        {
            result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
        }
    }
}

CallDotNetExample4.razor:

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

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

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

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

<p>
    @result
</p>

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

    protected override void OnInitialized()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
    }

    private async Task TriggerDotNetInstanceMethod()
    {
        if (jsInteropClasses is not null)
        {
            result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
        }
    }
}

CallDotNetExample4.razor:

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

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

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

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

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private JsInteropClasses3 jsInteropClasses;

    protected override void OnInitialized()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
    }

    private async Task TriggerDotNetInstanceMethod()
    {
        result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
    }
}

CallDotNetExample4.razor:

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

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

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

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

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private JsInteropClasses3 jsInteropClasses;

    protected override void OnInitialized()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
    }

    private async Task TriggerDotNetInstanceMethod()
    {
        result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
    }
}

L’image suivante montre le composant rendu avec le nom Amy Pond dans le champ Name. Une fois le bouton sélectionné, Hello, Amy Pond! s’affiche dans l’interface utilisateur :

Exemple de composant « CallDotNetExample4 » affiché

Le modèle précédent indiqué dans la classe JsInteropClasses3 peut également être implémenté entièrement dans un composant.

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

Pour éviter une fuite de mémoire et autoriser le nettoyage de la mémoire, la référence d’objet .NET créée par DotNetObjectReference est supprimée lorsque la référence d’objet sort de l’étendue avec la syntaxe using var.

La sortie affichée par le composant est Hello, Amy Pond! lorsque le nom Amy Pond est fourni dans le champ name.

Dans le composant précédent, la référence d’objet .NET est supprimée. Si une classe ou un composant ne supprime pas le DotNetObjectReference, supprimez-le du client en appelant dispose sur le DotNetObjectReference passé :

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

Dans l’exemple précédent :

  • L’espace réservé {JS FUNCTION NAME} est le nom de la fonction JS.
  • Le nom de variable dotNetHelper est arbitraire et peut être remplacé par n’importe quel nom de votre choix.
  • L’espace réservé {.NET METHOD ID} est l’identificateur de méthode .NET.

Classe d’assistance de méthode .NET d’instance de composant

Une classe d’assistance peut appeler une méthode d’instance .NET en tant que Action. Les classes d’assistance sont utiles dans les scénarios où l’utilisation de méthodes .NET statiques n’est pas applicable :

  • Lorsque plusieurs composants du même type sont rendus sur la même page.
  • Dans les applications côté serveur avec plusieurs utilisateurs utilisant simultanément le même composant.

Dans l’exemple suivant :

  • Le composant contient plusieurs ListItem1 composants.
  • Chaque composant ListItem1 est composé d’un message et d’un bouton.
  • Lorsqu’un bouton de composant ListItem1 est sélectionné, la méthode UpdateMessage de ce ListItem1 modifie le texte de l’élément de liste et masque le bouton.

La classe MessageUpdateInvokeHelper suivante gère une méthode .NET disponible pour JS, UpdateMessageCaller, pour appeler le Action spécifié lorsque la classe est instanciée.

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 fonction updateMessageCallerJS suivante appelle la méthode .NET UpdateMessageCaller.

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

Remarque

Pour obtenir une aide générale sur l’emplacement deJS et nos recommandations pour les applications de production, consultez Emplacement de JavaScript dans les applications Blazor ASP.NET Core.

Dans l’exemple précédent, le nom de variable dotNetHelper est arbitraire et peut être remplacé par n’importe quel nom de votre choix.

Le composant ListItem1 suivant est un composant partagé qui peut être utilisé n’importe quel nombre de fois dans un composant parent, et qui crée des éléments de liste (<li>...</li>) pour une liste HTML (<ul>...</ul> ou <ol>...</ol>). Chaque instance de composant ListItem1 établit une instance de MessageUpdateInvokeHelper avec une Action définie sur sa méthode UpdateMessage.

Lorsqu’un bouton InteropCall d’un composant ListItem1 est sélectionné, updateMessageCaller est appelé avec un DotNetObjectReference créé pour l’instance de MessageUpdateInvokeHelper. Cela permet au framework d’appeler UpdateMessageCaller sur le MessageUpdateInvokeHelper de cette instance ListItem1. Le DotNetObjectReference passé est supprimé dans 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 est appelé pour mettre à jour l’interface utilisateur lorsque message est défini dans UpdateMessage. Si StateHasChanged n’est pas appelé, Blazor n’a aucun moyen de savoir que l’interface utilisateur doit être mise à jour lorsque Action est appelé.

Le composant parent suivant comprend quatre éléments de liste, chacun étant une instance du composant 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’image suivante montre le composant parent rendu une fois le deuxième bouton InteropCall sélectionné :

  • Le deuxième composant ListItem1 a affiché le message UpdateMessage Called!.
  • Le bouton InteropCall du deuxième composant ListItem1 n’est pas visible, car la propriété CSS display du bouton est définie sur none.

Exemple de composant « CallDotNetExample6 » affiché

Méthode .NET d’instance de composant appelée à partir de DotNetObjectReference affectée à une propriété d’élément

L’affectation d’un DotNetObjectReference à une propriété d’un élément HTML permet d’appeler des méthodes .NET sur une instance de composant :

Comme dans la section de la classe d’assistance de méthode .NET de l’instance de composant, cette approche est utile dans les scénarios où l’utilisation de méthodes .NET statiques n’est pas applicable :

  • Lorsque plusieurs composants du même type sont rendus sur la même page.
  • Dans les applications côté serveur avec plusieurs utilisateurs utilisant simultanément le même composant.
  • La méthode .NET est appelée à partir d’un événement JS (par exemple, onclick), et non à partir d’un événement Blazor (par exemple, @onclick).

Dans l’exemple suivant :

  • Le composant contient plusieurs composants ListItem2, qui est un composant partagé.
  • Chaque composant ListItem2 est constitué d’un message d’élément de liste <span> et d’un deuxième <span> avec une propriété CSS display définie sur inline-block pour l’affichage.
  • Lorsqu’un élément de liste de composants ListItem2 est sélectionné, la méthode UpdateMessage de ce ListItem2 modifie le texte de l’élément de liste dans le premier <span> et masque la deuxième <span> en définissant sa propriété display sur none.

La fonction assignDotNetHelperJS suivante affecte la DotNetObjectReference à un élément d’une propriété nommée dotNetHelper. La fonction interopCallJS suivante utilise la DotNetObjectReference de l’élément passé pour appeler une méthode .NET nommée 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>

Remarque

Pour obtenir une aide générale sur l’emplacement deJS et nos recommandations pour les applications de production, consultez Emplacement de JavaScript dans les applications Blazor ASP.NET Core.

Dans l’exemple précédent, le nom de variable dotNetHelper est arbitraire et peut être remplacé par n’importe quel nom de votre choix.

Le composant ListItem2 suivant est un composant partagé qui peut être utilisé n’importe quel nombre de fois dans un composant parent, et qui crée des éléments de liste (<li>...</li>) pour une liste HTML (<ul>...</ul> ou <ol>...</ol>).

Chaque instance de composant ListItem2 appelle la fonction assignDotNetHelperJS dans OnAfterRenderAsync avec une référence d’élément (premier élément <span> de l’élément de liste) et l’instance de composant en tant que DotNetObjectReference.

Lorsqu’un <span> de message d’un composant ListItem2 est sélectionné, interopCall est appelé en passant l’élément <span> en tant que paramètre (this), ce qui appelle la méthode .NET UpdateMessage. Dans UpdateMessage, StateHasChanged est appelé pour mettre à jour l’interface utilisateur lorsque message est défini et que la propriété display du deuxième <span> est mise à jour. Si StateHasChanged n’est pas appelé, Blazor n’a aucun moyen de savoir que l’interface utilisateur doit être mise à jour lorsque la méthode est appelée.

La DotNetObjectReference est supprimée lorsque le composant est supprimé.

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

Le composant parent suivant comprend quatre éléments de liste, chacun étant une instance du composant 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>

Interopérabilité JS synchrone dans les composants côté client

Cette section s’applique uniquement aux composants côté client.

Les appels d’interopérabilité JS sont asynchrones, que le code appelé soit synchrone ou asynchrone. Les appels sont asynchrones pour garantir que les composants sont compatibles entre les modes de rendu côté serveur et côté client. Sur le serveur, tous les appels interop JS doivent être asynchrones car ils sont envoyés via une connexion réseau.

Si vous savez avec certitude que votre composant s'exécute uniquement sur WebAssembly, vous pouvez choisir d'effectuer des appels interop synchrones JS. Cela représente un peu moins de surcharge que d’effectuer des appels asynchrones, et peut entraîner moins de cycles de rendu, car il n’y a pas d’état intermédiaire lors de l’attente des résultats.

Pour effectuer un appel synchrone de JavaScript vers .NET dans un composant côté client, utilisez DotNet.invokeMethod à la place de DotNet.invokeMethodAsync.

Les appels synchrones fonctionnent si :

  • Le composant est uniquement rendu pour exécution sur WebAssembly.
  • La fonction appelée retourne une valeur de manière synchrone. La fonction n’est pas une méthode async et ne retourne pas de Task .NET ou Promise JavaScript.

Emplacement JavaScript

Chargez le code JavaScript (JS) en utilisant une des approches décrites par l’article sur l’emplacement de JavaScript :

L’utilisation de modules JS pour charger JS est décrite dans cet article dans la section Isolation JavaScript dans les modules JavaScript.

Avertissement

Placez une balise <script> dans un fichier de composant (.razor) uniquement si vous avez l’assurance que le composant adoptera le rendu statique côté serveur (SSR statique), car la balise <script> ne peut pas être mise à jour dynamiquement.

Avertissement

Ne placez pas une balise <script> dans un fichier de composant (.razor) car la balise <script> ne peut pas être mise à jour dynamiquement.

Isolation JavaScript dans les modules JavaScript

Blazor active l’isolation JavaScript (JS) dans les modules JavaScript standard (spécification ECMAScript). Le chargement de module JavaScript fonctionne de la même manière dans Blazor que pour d’autres types d’applications web, et vous êtes libre de personnaliser la façon dont les modules sont définis dans votre application. Pour obtenir un guide sur l’utilisation des modules JavaScript, consultez MDN Web Docs : modules JavaScript.

L’isolation JS offre les avantages suivants :

  • Le code JS importé ne pollue plus l’espace de noms global.
  • Les consommateurs d’une bibliothèque et de composants ne sont pas tenus d’importer le code JS associé.

Pour plus d’informations, consultez Appeler des fonctions JavaScript à partir de méthodes .NET dans ASP.NET Core Blazor.

L’importation dynamique avec l’import()opérateur est prise en charge avec ASP.NET Core et Blazor :

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

Dans l’exemple précédent, l’espace {CONDITION} réservé représente une vérification conditionnelle pour déterminer si le module doit être chargé.

Pour la compatibilité du navigateur, consultez Puis-je utiliser : modules JavaScript : importation dynamique.

Éviter les références d’objets circulaires

Les objets qui contiennent des références circulaires ne peuvent pas être sérialisés sur le client pour :

  • Les appels de méthode .NET.
  • Les appels de méthode JavaScript à partir de C# lorsque le type de retour a des références circulaires.

Prise en charge des tableaux d’octets

Blazor prend en charge l’interopérabilité JavaScript (JS) des tableaux d’octets optimisés, qui évite l’encodage/décodage des tableaux d’octets en Base64. L’exemple suivant utilise l’interopérabilité JS pour passer un tableau d’octets à .NET.

Fournissez une fonction sendByteArrayJS. La fonction est appelée statiquement, en incluant le paramètre de nom d’assembly dans l’appel invokeMethodAsync, par un bouton du composant et ne retourne pas de valeur :

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>

Remarque

Pour obtenir une aide générale sur l’emplacement deJS et nos recommandations pour les applications de production, consultez Emplacement de JavaScript dans les applications Blazor ASP.NET Core.

CallDotnet8.razor:

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

<PageTitle>Call .NET 8</PageTitle>

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

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

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

@code {
    private IJSObjectReference? module;

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

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

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

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

Pour plus d’informations sur l’utilisation d’un tableau d’octets lors de l’appel de JavaScript à partir de .NET, consultez Appeler des fonctions JavaScript à partir de méthodes .NET dans ASP.NET Core Blazor.

Diffuser en continu de JavaScript vers .NET

Blazor prend en charge la diffusion en continu de données directement de JavaScript vers .NET. Les flux sont demandés à l’aide de l’interface Microsoft.JSInterop.IJSStreamReference.

Microsoft.JSInterop.IJSStreamReference.OpenReadStreamAsync retourne un Stream et utilise les paramètres suivants :

  • maxAllowedSize : nombre maximal d’octets autorisé pour l’opération de lecture à partir de JavaScript, 512 000 octets par défaut si non spécifié.
  • cancellationToken : CancellationToken pour annuler la lecture.

En JavaScript :

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

Dans le code 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);

Dans l'exemple précédent :

  • JS est un instance IJSRuntime injectée. IJSRuntime est inscrit par le framework Blazor.
  • Le dataReferenceStream est écrit sur le disque (file.txt) au chemin du dossier temporaire de l’utilisateur actuel (GetTempPath).

Appeler des fonctions JavaScript à partir de méthodes .NET dans ASP.NET Core Blazor couvre l’opération inverse, la diffusion en continu de .NET vers JavaScript à l’aide d’une DotNetStreamReference.

Chargements de fichiers ASP.NET Core Blazor explique comment charger un fichier dans Blazor. Pour obtenir un exemple de formulaire qui diffuse en continu des données <textarea> dans un composant côté serveur, consultez Résoudre les problèmes liés aux formulaires Blazor ASP.NET Core.

JavaScript [JSImport]/[JSExport] interop

Cette section s’applique aux composants côté client.

En guise d’alternative à l’interaction avec JavaScript (JS) dans les composants côté client à l’aide du mécanisme d’interopérabilité JS de Blazor basé sur l’interface IJSRuntime, une API d’interopérabilité JS[JSImport]/[JSExport] est disponible pour les applications ciblant .NET 7 ou version ultérieure.

Pour plus d’informations, consultez Interopérabilité JSImport/JSExport JavaScript avec ASP.NET Core Blazor.

Suppression des références d’objets d’interopérabilité JavaScript

Les exemples des articles d’interopérabilité JavaScript (JS) illustrent des modèles de suppression d’objets classiques :

Les références d’objet d’interopérabilité JS sont implémentées en tant que carte avec pour clé un identificateur sur le côté de l’appel d’interopérabilité JS qui crée la référence. Lorsque l’élimination de l’objet est lancée du côté .NET ou JS, Blazor supprime l’entrée de la carte, et l’objet peut être récupéré en mémoire tant qu’aucune autre référence forte à l’objet n’est présente.

Au minimum, éliminez toujours les objets créés côté .NET pour éviter les fuites de mémoire managée .NET.

Tâches de nettoyage de modèle DOM lors de la suppression des composants

Pour plus d’informations, consultez Interopérabilité JavaScript et ASP.NET Core Blazor (interopérabilité JS).

Appels d’interopérabilité JavaScript sans circuit

Pour plus d’informations, consultez Interopérabilité JavaScript et ASP.NET Core Blazor (interopérabilité JS).

Ressources supplémentaires