Partage via


Appelez des méthodes .NET depuis des fonctions JavaScript dans ASP.NET Core Blazor

Note

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

Warning

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

Cette rubrique explique comment appeler des méthodes .NET depuis JavaScript (JS).

Pour savoir comment appeler des fonctions JS depuis .NET, veuillez consulter la section Appeler des fonctions JavaScript depuis des méthodes .NET dans ASP.NET Core Blazor.

Appeler une méthode .NET statique

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

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

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

Dans l’exemple suivant :

  • L’espace réservé {PACKAGE ID/ASSEMBLY NAME} correspond à l’ID de package du projet (<PackageId> dans le fichier projet) pour une bibliothèque ou au nom de l’assembly pour une application.
  • L’espace réservé {.NET METHOD ID} correspond à l’identifiant de la méthode .NET.
  • Les espaces réservés {ARGUMENTS} sont des arguments facultatifs, séparés par des virgules, à transmettre à la méthode, chacun devant être sérialisable en JSON.
DotNet.invokeMethodAsync('{PACKAGE ID/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 posséder [JSInvokable] comme attribut.

Dans l’exemple suivant :

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

Note

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

Dans le composant suivant, la méthode C# ReturnArrayAsync retourne un tableau int. [JSInvokable] est l' attribut appliqué à la méthode, ce qui la rend appelable par 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 comme gestionnaire.

La fonction returnArrayAsyncJS appelle la méthode .NET ReturnArrayAsync du composant, qui journalise le résultat dans la console des outils de développement web du navigateur. BlazorSample correspond au 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 comme gestionnaire.

La fonction returnArrayAsyncJS appelle la méthode .NET ReturnArrayAsync du composant, qui journalise le résultat dans la console des outils de développement web du navigateur. BlazorSample correspond au 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 <button> de l’élément onclick correspond à l’attribution du gestionnaire d’événement onclick de JavaScript pour le traitement des événements click, et non à l’attribut de directive Blazor de @onclick. La fonction returnArrayAsyncJS est affectée comme gestionnaire.

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

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

Note

Pour des conseils généraux concernant l’emplacement JS et nos recommandations pour les applications de production, veuillez consulter la section Emplacement JavaScript dans les applications ASP.NET Core Blazor.

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 des données de sortie varie légèrement selon les navigateurs. Voici un exemple montrant 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 sous forme d’arguments.

Pour démontrer la transmission de données à .NET, transmettez une position de départ à la méthode ReturnArrayAsync où 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 appelable du composant reçoit la position de départ et construit le tableau à partir de celle-ci. Le tableau est retourné pour journalisation dans la console :

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

Après la re-compilation de l’application et l’actualisation du navigateur, la sortie suivante apparaît dans la console du navigateur lorsque le bouton est sélectionné :

Array(3) [ 14, 15, 16 ]

L’identifiant de la méthode .NET pour l’appel JS est le nom de la méthode .NET, mais vous pouvez spécifier un identifiant différent à l’aide du constructeur d’attribut [JSInvokable]. Dans l’exemple suivant, DifferentMethodName est l’identifiant de méthode affecté à 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)

Note

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

Méthode ReturnArrayAsync composée avec les 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, veuillez consulter la section Programmation asynchrone avec async et await du guide C#.

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

Appelez DotNet.createJSObjectReference(jsObject) pour construire une référence d’objet JS afin de la transmettre à .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 non sérialisable window à .NET, qui la reçoit dans la méthode C# ReceiveWindowObject sous la forme d’un IJSObjectReference :

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

Dans l’exemple précédent, l’espace réservé {PACKAGE ID/ASSEMBLY NAME} correspond à l’ID de package du projet (<PackageId> dans le fichier projet) pour une bibliothèque ou au nom de l’assembly pour une application.

Note

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

Le maintien d’une référence à un JSObjectReference nécessite sa suppression pour éviter les fuites de mémoire JS côté 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('{PACKAGE ID/ASSEMBLY NAME}', 'ReceiveWindowObject', jsObjectReference);

DotNet.disposeJSObjectReference(jsObjectReference);

Dans l’exemple précédent, l’espace réservé {PACKAGE ID/ASSEMBLY NAME} correspond à l’ID de package du projet (<PackageId> dans le fichier projet) pour une bibliothèque ou au nom de l’assembly pour une application.

Appelez DotNet.createJSStreamReference(streamReference) pour construire une référence de flux JS afin de la transmettre à .NET, où streamReference est un ArrayBuffer, Blob ou tout tableau typé (type array), tel que 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 depuis JavaScript (JS) :

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

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

    Dans l’exemple suivant :

    • dotNetHelper est un DotNetObjectReference.
    • L’espace réservé {.NET METHOD ID} correspond à l’identifiant de la méthode .NET.
    • Les espaces réservés {ARGUMENTS} sont des arguments facultatifs, séparés par des virgules, à transmettre à la méthode, chacun devant être sérialisable en JSON.
    dotNetHelper.invokeMethodAsync('{.NET METHOD ID}', {ARGUMENTS});
    

    Note

    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 seulement) 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 cette rubrique présentent diverses approches pour appeler une méthode .NET d’instance :

Éviter la suppression des méthodes .NET appelables par JavaScript

Cette section s’applique aux applications côté client avec compilation anticipée (AOT) et reliage dynamique à l’exécution activés.

Plusieurs exemples dans les sections suivantes sont basés sur une approche par instance de classe, où la méthode .NET appelable par JavaScript, marquée par [JSInvokable] comme attribut, 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 le reliage/suppression à l’exécution. Pour protéger les méthodes .NET contre la suppression hors des composants Razor, implémentez les méthodes avec DynamicDependency comme attribut 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, veuillez consulter la section Préparer des bibliothèques .NET à la suppression : DynamicDependency.

Transmettre un DotNetObjectReference à une fonction JavaScript individuelle

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

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

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

Note

Pour des conseils généraux concernant l’emplacement JS et nos recommandations pour les applications de production, veuillez consulter la section Emplacement JavaScript dans les applications ASP.NET Core Blazor.

Dans l’exemple précédent, le nom de variable dotNetHelper est arbitraire et peut être remplacé par tout nom préféré.

Pour le composant suivant :

  • Le composant possède une méthode .NET appelable via JS nommée GetHelloMessage.
  • Lorsque le bouton Trigger .NET instance method est sélectionné, la fonction JSsayHello1 est appelée avec le DotNetObjectReference.
  • sayHello1:
    • Appelle GetHelloMessage et reçoit le résultat du message.
    • Retourne le résultat du message à la méthode appelante TriggerDotNetInstanceMethod.
  • Le message retourné par sayHello1 dans result est affiché à l’utilisateur.
  • Pour éviter une fuite de mémoire et permettre le ramassage de déchets, 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 tout nom préféré.

Utilisez les indications suivantes pour transmettre des arguments à une méthode d’instance :

Ajoutez des paramètres à l’appel de la méthode .NET. Dans l’exemple suivant, un nom est transmis à la méthode. Ajoutez des paramètres supplémentaires à la liste si nécessaire.

<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 tout nom préféré.

Fournissez la liste de 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 tout nom préféré.

Transmettre un DotNetObjectReference à une classe contenant plusieurs fonctions JavaScript

L’exemple de cette section montre comment transmettre un DotNetObjectReference à une classe JavaScript (JS) contenant plusieurs fonctions.

Créez et transmettez un DotNetObjectReference à partir de la méthode de cycle de vie OnAfterRenderAsync à une classe JS afin que plusieurs fonctions puissent l’utiliser. Assurez-vous que le code .NET supprime le 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 l’attribut de directive Blazor de @onclick.

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 une instance injectée de IJSRuntime. IJSRuntime est inscrit par le framework Blazor.
  • Le nom de variable dotNetHelper est arbitraire et peut être remplacé par tout nom préféré.
  • Le composant doit supprimer explicitement le DotNetObjectReference pour permettre le garbage collection (GC) et éviter une fuite de mémoire.
  • JSDisconnectedException est capturé lors de la suppression du module si le circuit Blazor de SignalR est perdu. Si le code précédent est utilisé dans une application Blazor WebAssembly, il n’existe aucune connexion SignalR à perdre. Vous pouvez donc supprimer le bloc try-catch 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 tout nom préféré.

@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 une instance injectée de IJSRuntime. IJSRuntime est inscrit par le framework Blazor.
  • Le nom de variable dotNetHelper est arbitraire et peut être remplacé par tout nom préféré.
  • Le composant doit supprimer explicitement le DotNetObjectReference pour permettre le garbage collection (GC) et éviter 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 la localiser pour l’interopérabilité JS.
  • Le nom de variable dotNetHelper est arbitraire et peut être remplacé par tout nom préféré.

Note

Pour des conseils généraux concernant l’emplacement JS et nos recommandations pour les applications de production, veuillez consulter la section Emplacement JavaScript dans les applications ASP.NET Core Blazor.

Appeler des méthodes de classe générique .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 générique suivante (GenericType<TValue>) :

  • La classe comporte un seul paramètre de type (TValue) avec une seule propriété générique Value.
  • La classe comporte 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 depuis newValue.
    • UpdateAsync met à jour de manière asynchrone la valeur de Value depuis newValue après avoir créé une tâche awaitable avec Task.Yield qui restitue de manière asynchrone le contrôle au contexte courant lorsqu’elle est attendue.
  • 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 uniquement à des fins de démonstration. Les applications de production évitent généralement l’écriture dans la console au profit du framework de journalisation de l’application. Pour plus d’informations, consultez ASP.NET Core Blazor journalisation et journalisation dans .NET et ASP.NET Core.

Note

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 démontrent des génériques fermés, mais l’appel de JS interop 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éthodes .NET statiques, qui ont été décrits plus tôt dans cette rubrique.

Si vous souhaitez en savoir plus, 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 générique sont appelées avec des arguments représentant des chaînes de caractères et des nombres.
  • Les composants côté client prennent en charge l’appel synchrone des méthodes .NET avec invokeMethod. syncInterop reçoit une valeur booléenne indiquant si l’interopérabilité JS s’effectue côté client. Lorsque syncInterop vaut true, invokeMethod est appelé en toute sécurité. 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 la 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 de faire correspondre l’appel de la fonction JS avec l’appel de la méthode .NET (également écrit dans la console côté .NET). Le code de production n’écrit généralement pas dans la console, que ce soit côté client ou côté serveur. Les applications de production s’appuient généralement sur le framework de journalisation de l’application. Pour plus d’informations, consultez ASP.NET Core Blazor journalisation et journalisation dans .NET 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>

Note

Pour des conseils généraux concernant l’emplacement JS et nos recommandations pour les applications de production, veuillez consulter la section Emplacement JavaScript dans les applications ASP.NET Core Blazor.

Dans le composant GenericsExample suivant :

  • La fonction JSinvokeMethodsAsync 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 des 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 injectée de IJSRuntime. IJSRuntime est inscrit par le framework Blazor.

L’exemple suivant montre une sortie typique 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 : Mise à jour : GenericType<System.String> : string 26784
JS: invokeMethodAsync :Update(14107)
.NET : Mise à jour : GenericType<System.Int32> : 14107
JS: invokeMethodAsync :UpdateAsync(48995)
JS: invokeMethod :Update(12872)
.NET : Mise à jour : 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 typique d’un composant côté serveur :

JS : invokeMethodAsync\:Update('string 34809')
.NET : Mise à jour : GenericType<System.String> : string 34809
JS: invokeMethodAsync :UpdateAsync('string 93059')
JS: invokeMethodAsync :Update(41997)
.NET : Mise à jour : 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 selon plusieurs facteurs, notamment l’ordonnancement des threads et la vitesse d’exécution des méthodes. Il n’est pas possible de prévoir de manière fiable l’ordre d’exécution des appels de méthodes asynchrones.

Exemples d’instance de classe

La fonction sayHello1JS suivante :

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

Note

Pour des conseils généraux concernant l’emplacement JS et nos recommandations pour les applications de production, veuillez consulter la section Emplacement JavaScript dans les applications ASP.NET Core Blazor.

Dans l’exemple précédent, le nom de variable dotNetHelper est arbitraire et peut être remplacé par tout nom préféré.

La classe HelloHelper suivante possède une méthode .NET appelable via JS nommée GetHelloMessage. Lors de la création de HelloHelper, le nom dans la propriété Name est utilisé pour retourner un message depuis 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 dans la classe JsInteropClasses3 suivante appelle la fonction JSsayHello1 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 permettre le garbage collection, la référence d’objet .NET créée par DotNetObjectReference est supprimée lorsque la référence sort de la portée à l’aide de 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. Après sélection du bouton, Hello, Amy Pond! est affiché dans l’interface utilisateur :

Exemple du composant rendu ‘CallDotNetExample4’

Le modèle précédent montré dans la classe JsInteropClasses3 peut également être entièrement implémenté 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 permettre le garbage collection, la référence d’objet .NET créée par DotNetObjectReference est supprimée lorsque la référence sort de la portée à l’aide de la syntaxe using var.

La sortie affichée par le composant est Hello, Amy Pond! lorsque le nom Amy Pond est saisi 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 côté client en appelant dispose sur le DotNetObjectReference transmis :

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} correspond au nom de la fonction JS.
  • Le nom de variable dotNetHelper est arbitraire et peut être remplacé par tout nom préféré.
  • L’espace réservé {.NET METHOD ID} correspond à l’identifiant de la méthode .NET.

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

Une classe d’assistance peut appeler une méthode .NET d’instance comme un Action. Les classes d’assistance sont utiles dans des 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 composants ListItem1.
  • Chaque composant ListItem1 se compose d’un message et d’un bouton.
  • Lorsqu’un bouton d’un composant ListItem1 est sélectionné, la méthode ListItem1 de ce UpdateMessage modifie le texte de l’élément de liste et masque le bouton.

La classe MessageUpdateInvokeHelper suivante conserve une méthode .NET appelable via JS, UpdateMessageCaller, pour appeler le Action spécifié lors de l’instanciation de la classe.

MessageUpdateInvokeHelper.cs :

using Microsoft.JSInterop;

namespace BlazorSample;

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

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

namespace BlazorSample;

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

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

public class MessageUpdateInvokeHelper
{
    private Action action;

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

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

public class MessageUpdateInvokeHelper
{
    private Action action;

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

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

public class MessageUpdateInvokeHelper
{
    private Action action;

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

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

public class MessageUpdateInvokeHelper
{
    private Action action;

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

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

La fonction updateMessageCallerJS suivante appelle la méthode .NET UpdateMessageCaller.

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

Note

Pour des conseils généraux concernant l’emplacement JS et nos recommandations pour les applications de production, veuillez consulter la section Emplacement JavaScript dans les applications ASP.NET Core Blazor.

Dans l’exemple précédent, le nom de variable dotNetHelper est arbitraire et peut être remplacé par tout nom préféré.

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

Lorsque le bouton ListItem1 d’un composant InteropCall est sélectionné, updateMessageCaller est appelé avec un DotNetObjectReference créé pour l’instance MessageUpdateInvokeHelper. Cela permet au framework d’appeler UpdateMessageCaller sur l’instance ListItem1 du MessageUpdateInvokeHelper concerné. Le DotNetObjectReference transmis 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 ne peut pas savoir que l’interface utilisateur doit être mise à jour lorsque Action est invoqué.

Le composant parent suivant inclut 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 après sélection du deuxième bouton InteropCall :

  • 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 du composant rendu ‘CallDotNetExample6’

Méthode .NET d’instance de composant appelée depuis DotNetObjectReference affecté à 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 pour l’approche décrite dans la section Classe d’assistance pour méthode .NET d’instance de composant, cette méthode est utile dans les cas où les méthodes .NET statiques ne conviennent pas :

  • 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 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 composé d’un message d’élément de liste <span> et d’un second <span> avec une propriété CSS display définie sur inline-block pour l’affichage.
  • Lorsqu’un élément de liste d’un composant ListItem2 est sélectionné, la méthode ListItem2 de ce UpdateMessage modifie le texte de l’élément de liste dans le premier <span> et masque le second <span> en définissant sa propriété display sur none.

La fonction assignDotNetHelperJS suivante assigne le DotNetObjectReference à un élément dans une propriété nommée dotNetHelper. La fonction interopCallJS suivante utilise le DotNetObjectReference de l’élément transmis 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>

Note

Pour des conseils généraux concernant l’emplacement JS et nos recommandations pour les applications de production, veuillez consulter la section Emplacement JavaScript dans les applications ASP.NET Core Blazor.

Dans l’exemple précédent, le nom de variable dotNetHelper est arbitraire et peut être remplacé par tout nom préféré.

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

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

Lorsque l’élément ListItem2 de message d’un composant <span> est sélectionné, la fonction interopCallinteropCall est appelée en transmettant l’élément <span> comme 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 second <span> est mise à jour. Si StateHasChanged n’est pas appelé, Blazor ne peut pas savoir que l’interface utilisateur doit être mise à jour lorsque la méthode est invoquée.

Le DotNetObjectReference est supprimé lors de la suppression du composant.

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 inclut 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 ne s'applique qu'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 d’interopérabilité JS doivent être asynchrones, car ils sont envoyés via une connexion réseau.

Si vous êtes certain que votre composant s’exécute uniquement sur WebAssembly, vous pouvez choisir de faire des appels d’interopérabilité JS synchrones. 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 au lieu de DotNet.invokeMethodAsync.

Les appels synchrones fonctionnent si :

  • Le composant est rendu uniquement 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 de Promise JavaScript.

Emplacement JavaScript

Chargez du code JavaScript (JS) à l’aide de l’une des approches décrites dans l’article sur l’emplacement JavaScript :

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

Warning

Placez une balise <script> dans un fichier composant (.razor) uniquement si le composant adopte le rendu statique côté serveur (static SSR), car la balise <script> ne peut pas être mise à jour dynamiquement.

Warning

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 (elle inclut le nom de l’assembly dans l’appel invokeMethodAsync) par un bouton dans le 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>

Note

Pour des conseils généraux concernant l’emplacement JS et nos recommandations pour les applications de production, veuillez consulter la section Emplacement JavaScript dans les applications ASP.NET Core Blazor.

CallDotnet8.razor :

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

<PageTitle>Call .NET 8</PageTitle>

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

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

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

@code {
    private IJSObjectReference? module;

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

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

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

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

CallDotnet8.razor :

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

<PageTitle>Call .NET 8</PageTitle>

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

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

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

@code {
    private IJSObjectReference? module;

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

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

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

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

CallDotNetExample8.razor :

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

<PageTitle>Call .NET 8</PageTitle>

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

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

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

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

CallDotNetExample8.razor :

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

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

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

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

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

Pour savoir comment utiliser un tableau d’octets lors d’un appel de JavaScript depuis .NET, veuillez consulter la section Appeler des fonctions JavaScript depuis des méthodes .NET dans ASP.NET Core Blazor.

Diffuser en continu de JavaScript vers .NET

Blazor prend en charge le streaming de données directement de JavaScript vers .NET. Les flux sont demandés via l’interface Microsoft.JSInterop.IJSStreamReference.

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

  • maxAllowedSize : nombre maximal d’octets autorisés pour l’opération de lecture depuis JavaScript, par défaut 512 000 octets si non spécifié.
  • cancellationToken : un CancellationToken permettant d’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 une instance injectée de IJSRuntime. IJSRuntime est inscrit par le framework Blazor.
  • Le dataReferenceStream est écrit sur le disque (file.txt) dans le chemin du dossier temporaire de l’utilisateur actuel (GetTempPath).

Appeler des fonctions JavaScript depuis des méthodes .NET dans ASP.NET Core Blazor couvre l’opération inverse, à savoir le streaming de .NET vers JavaScript à l’aide d’un DotNetStreamReference.

Uploads de fichiers dans ASP.NET Core Blazor couvre comment téléverser un fichier dans Blazor. Pour consulter un exemple de formulaire qui streame des données <textarea> dans un composant côté serveur, veuillez consulter la section Résolution des problèmes de formulaires ASP.NET Core Blazor.

Interopérabilité JavaScript [JSImport]/[JSExport]

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

Pour une alternative à l’interaction avec JavaScript (JS) dans les composants côté client en utilisant le mécanisme d’interopérabilité Blazor de JS basé sur l’interface IJSRuntime, une API d’interopérabilité JS[JSImport]/[JSExport] est disponible pour les applications ciblant .NET 7 ou les versions ultérieures.

Pour plus d’informations, veuillez consulter la section Interopérabilité JavaScript JSImport/JSExport 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 identifiant du 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