Aufrufen von .NET-Methoden von JavaScript-Funktionen in ASP.NET Core BlazorCall .NET methods from JavaScript functions in ASP.NET Core Blazor

Von Javier Calvarro Nelson, Daniel Roth, Shashikant Rudrawadi und Luke LathamBy Javier Calvarro Nelson, Daniel Roth, Shashikant Rudrawadi, and Luke Latham

Eine Blazor-App kann JavaScript-Funktionen über .NET-Methoden und .NET-Methoden über JavaScript-Funktionen aufrufen.A Blazor app can invoke JavaScript functions from .NET methods and .NET methods from JavaScript functions. Diese Szenarios werden als JavaScript-Interoperabilität (JS Interop) bezeichnet.These scenarios are called JavaScript interoperability (JS interop).

In diesem Artikel wird das Aufrufen von .NET-Methoden über JavaScript behandelt.This article covers invoking .NET methods from JavaScript. Informationen zum Aufrufen von JavaScript-Funktionen über .NET finden Sie unter Aufrufen von JavaScript-Funktionen über .NET-Methoden in ASP.NET Core Blazor.For information on how to call JavaScript functions from .NET, see Aufrufen von JavaScript-Funktionen über .NET-Methoden in ASP.NET Core Blazor.

Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)View or download sample code (how to download)

Statischer .NET-MethodenaufrufStatic .NET method call

Um eine statische .NET-Methode über JavaScript aufzurufen, verwenden Sie die Funktionen DotNet.invokeMethod oder DotNet.invokeMethodAsync.To invoke a static .NET method from JavaScript, use the DotNet.invokeMethod or DotNet.invokeMethodAsync functions. Übergeben Sie den Bezeichner der statischen Methode, die Sie aufrufen möchten, den Namen der Assembly, die die Funktion enthält, und alle Argumente.Pass in the identifier of the static method you wish to call, the name of the assembly containing the function, and any arguments. Die asynchrone Version wird bevorzugt, um Blazor Server-Szenarien zu unterstützen.The asynchronous version is preferred to support Blazor Server scenarios. Die .NET-Methode muss öffentlich und statisch sein sowie das Attribut [JSInvokable] aufweisen.The .NET method must be public, static, and have the [JSInvokable] attribute. Das Aufrufen offener generischer Methoden wird derzeit nicht unterstützt.Calling open generic methods isn't currently supported.

Die Beispiel-App enthält eine C#-Methode zur Rückgabe eines int-Arrays.The sample app includes a C# method to return an int array. Das [JSInvokable]-Attribut wird auf die Methode angewendet.The [JSInvokable] attribute is applied to the method.

Pages/JsInterop.razor:Pages/JsInterop.razor:

<button type="button" class="btn btn-primary"
        onclick="exampleJsFunctions.returnArrayAsyncJs()">
    Trigger .NET static method ReturnArrayAsync
</button>

@code {
    [JSInvokable]
    public static Task<int[]> ReturnArrayAsync()
    {
        return Task.FromResult(new int[] { 1, 2, 3 });
    }
}

Das dem Client zugestellte JavaScript ruft die C# .NET-Methode auf.JavaScript served to the client invokes the C# .NET method.

wwwroot/exampleJsInterop.js:wwwroot/exampleJsInterop.js:

window.exampleJsFunctions = {
  showPrompt: function (text) {
    return prompt(text, 'Type your name here');
  },
  displayWelcome: function (welcomeMessage) {
    document.getElementById('welcome').innerText = welcomeMessage;
  },
  returnArrayAsyncJs: function () {
    DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
      .then(data => {
        data.push(4);
          console.log(data);
      });
  },
  sayHello: function (dotnetHelper) {
    return dotnetHelper.invokeMethodAsync('SayHello')
      .then(r => console.log(r));
  }
};

Überprüfen Sie bei Auswahl der Schaltfläche Trigger .NET static method ReturnArrayAsync die Konsolenausgabe in den Web Developer Tools des Browsers.When the Trigger .NET static method ReturnArrayAsync button is selected, examine the console output in the browser's web developer tools.

Die Konsolenausgabe lautet:The console output is:

Array(4) [ 1, 2, 3, 4 ]

Der vierte Arraywert wird in das von ReturnArrayAsync zurückgegebene Array (data.push(4);) gepusht.The fourth array value is pushed to the array (data.push(4);) returned by ReturnArrayAsync.

Standardmäßig ist der Methodenbezeichner der Methodenname, aber Sie können mit dem Attributkonstruktor [JSInvokable] einen anderen Bezeichner angeben:By default, the method identifier is the method name, but you can specify a different identifier using the [JSInvokable] attribute constructor:

@code {
    [JSInvokable("DifferentMethodName")]
    public static Task<int[]> ReturnArrayAsync()
    {
        return Task.FromResult(new int[] { 1, 2, 3 });
    }
}

In der clientseitigen JavaScript-Datei:In the client-side JavaScript file:

returnArrayAsyncJs: function () {
  DotNet.invokeMethodAsync('{APP ASSEMBLY}', 'DifferentMethodName')
    .then(data => {
      data.push(4);
      console.log(data);
    });
}

Der Platzhalter {APP ASSEMBLY} ist der App-Assemblyname der App (z. B. BlazorSample).The placeholder {APP ASSEMBLY} is the app's app assembly name (for example, BlazorSample).

Aufruf der InstanzmethodeInstance method call

Sie können auch .NET-Instanzmethoden von JavaScript aus aufrufen.You can also call .NET instance methods from JavaScript. So rufen Sie .NET-Instanzmethoden von JavaScript aus aufTo invoke a .NET instance method from JavaScript:

  • Übergeben Sie die .NET-Instanz durch Verweis auf JavaScript:Pass the .NET instance by reference to JavaScript:
  • Rufen Sie .NET-Instanzmethoden für die Instanz mit den Funktionen invokeMethod oder invokeMethodAsync auf.Invoke .NET instance methods on the instance using the invokeMethod or invokeMethodAsync functions. Die .NET-Instanz kann auch als Argument übergeben werden, wenn andere .NET-Methoden von JavaScript aus aufgerufen werden.The .NET instance can also be passed as an argument when invoking other .NET methods from JavaScript.

Hinweis

Die Beispiel-App protokolliert Nachrichten an die clientseitige Konsole.The sample app logs messages to the client-side console. Für die folgenden Beispiele, die durch die Beispiel-App veranschaulicht werden, untersuchen Sie die Konsolenausgabe des Browsers in den Entwicklertools des Browsers.For the following examples demonstrated by the sample app, examine the browser's console output in the browser's developer tools.

Bei Auswahl der Schaltfläche Trigger .NET instance method HelloHelper.SayHello wird ExampleJsInterop.CallHelloHelperSayHello aufgerufen und der Name Blazor an die Methode übergeben.When the Trigger .NET instance method HelloHelper.SayHello button is selected, ExampleJsInterop.CallHelloHelperSayHello is called and passes a name, Blazor, to the method.

Pages/JsInterop.razor:Pages/JsInterop.razor:

<button type="button" class="btn btn-primary" @onclick="TriggerNetInstanceMethod">
    Trigger .NET instance method HelloHelper.SayHello
</button>

@code {
    public async Task TriggerNetInstanceMethod()
    {
        var exampleJsInterop = new ExampleJsInterop(JSRuntime);
        await exampleJsInterop.CallHelloHelperSayHello("Blazor");
    }
}

CallHelloHelperSayHello ruft die JavaScript-Funktion sayHello mit einer neuen Instanz von HelloHelper auf.CallHelloHelperSayHello invokes the JavaScript function sayHello with a new instance of HelloHelper.

JsInteropClasses/ExampleJsInterop.cs:JsInteropClasses/ExampleJsInterop.cs:

public class ExampleJsInterop : IDisposable
{
    private readonly IJSRuntime jsRuntime;
    private DotNetObjectReference<HelloHelper> objRef;

    public ExampleJsInterop(IJSRuntime jsRuntime)
    {
        this.jsRuntime = jsRuntime;
    }

    public ValueTask<string> CallHelloHelperSayHello(string name)
    {
        objRef = DotNetObjectReference.Create(new HelloHelper(name));

        return jsRuntime.InvokeAsync<string>(
            "exampleJsFunctions.sayHello",
            objRef);
    }

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

wwwroot/exampleJsInterop.js:wwwroot/exampleJsInterop.js:

window.exampleJsFunctions = {
  showPrompt: function (text) {
    return prompt(text, 'Type your name here');
  },
  displayWelcome: function (welcomeMessage) {
    document.getElementById('welcome').innerText = welcomeMessage;
  },
  returnArrayAsyncJs: function () {
    DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
      .then(data => {
        data.push(4);
          console.log(data);
      });
  },
  sayHello: function (dotnetHelper) {
    return dotnetHelper.invokeMethodAsync('SayHello')
      .then(r => console.log(r));
  }
};

Der Name wird an den Konstruktor von HelloHelper übergeben, der die Eigenschaft HelloHelper.Name festlegt.The name is passed to HelloHelper's constructor, which sets the HelloHelper.Name property. Wenn die JavaScript-Funktion sayHello ausgeführt wird, gibt HelloHelper.SayHello die Nachricht Hello, {Name}! zurück, die von der JavaScript-Funktion in die Konsole geschrieben wird.When the JavaScript function sayHello is executed, HelloHelper.SayHello returns the Hello, {Name}! message, which is written to the console by the JavaScript function.

JsInteropClasses/HelloHelper.cs:JsInteropClasses/HelloHelper.cs:

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

    public string Name { get; set; }

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

Konsolenausgabe in den Webentwicklertools des Browsers:Console output in the browser's web developer tools:

Hello, Blazor!

Verwenden Sie einen der folgenden Ansätze, um einen Arbeitsspeicherverlust zu vermeiden und die Garbage Collection für eine Komponente zu ermöglichen, die ein DotNetObjectReference erstellt:To avoid a memory leak and allow garbage collection on a component that creates a DotNetObjectReference, adopt one of the following approaches:

  • Löschen Sie das Objekt in der Klasse, die die DotNetObjectReference-Instanz erstellt hat:Dispose of the object in the class that created the DotNetObjectReference instance:

    public class ExampleJsInterop : IDisposable
    {
        private readonly IJSRuntime jsRuntime;
        private DotNetObjectReference<HelloHelper> objRef;
    
        public ExampleJsInterop(IJSRuntime jsRuntime)
        {
            this.jsRuntime = jsRuntime;
        }
    
        public ValueTask<string> CallHelloHelperSayHello(string name)
        {
            objRef = DotNetObjectReference.Create(new HelloHelper(name));
    
            return jsRuntime.InvokeAsync<string>(
                "exampleJsFunctions.sayHello",
                objRef);
        }
    
        public void Dispose()
        {
            objRef?.Dispose();
        }
    }
    

    Das vorhergehende Muster, das in der Klasse ExampleJsInterop gezeigt wird, kann auch in einer Komponente implementiert werden:The preceding pattern shown in the ExampleJsInterop class can also be implemented in a component:

    @page "/JSInteropComponent"
    @using {APP ASSEMBLY}.JsInteropClasses
    @implements IDisposable
    @inject IJSRuntime JSRuntime
    
    <h1>JavaScript Interop</h1>
    
    <button type="button" class="btn btn-primary" @onclick="TriggerNetInstanceMethod">
        Trigger .NET instance method HelloHelper.SayHello
    </button>
    
    @code {
        private DotNetObjectReference<HelloHelper> objRef;
    
        public async Task TriggerNetInstanceMethod()
        {
            objRef = DotNetObjectReference.Create(new HelloHelper("Blazor"));
    
            await JSRuntime.InvokeAsync<string>(
                "exampleJsFunctions.sayHello",
                objRef);
        }
    
        public void Dispose()
        {
            objRef?.Dispose();
        }
    }
    

    Der Platzhalter {APP ASSEMBLY} ist der App-Assemblyname der App (z. B. BlazorSample).The placeholder {APP ASSEMBLY} is the app's app assembly name (for example, BlazorSample).

  • Wenn die Komponente oder Klasse die DotNetObjectReference-Instanz nicht löscht, löschen Sie das Objekt im Client, indem Sie .dispose() aufrufen:When the component or class doesn't dispose of the DotNetObjectReference, dispose of the object on the client by calling .dispose():

    window.myFunction = (dotnetHelper) => {
      dotnetHelper.invokeMethod('{APP ASSEMBLY}', 'MyMethod');
      dotnetHelper.dispose();
    }
    

Aufrufen der KomponenteninstanzmethodeComponent instance method call

So rufen Sie die .NET-Methoden einer Komponente auf:To invoke a component's .NET methods:

  • Verwenden Sie die invokeMethod- oder invokeMethodAsync-Funktion, um einen statischen Methodenaufruf an die Komponente auszuführen.Use the invokeMethod or invokeMethodAsync function to make a static method call to the component.
  • Die statische Methode der Komponente umschließt den Aufruf der Instanzmethode als aufgerufene Action.The component's static method wraps the call to its instance method as an invoked Action.

In der clientseitigen JavaScript-Datei:In the client-side JavaScript:

function updateMessageCallerJS() {
  DotNet.invokeMethod('{APP ASSEMBLY}', 'UpdateMessageCaller');
}

Der Platzhalter {APP ASSEMBLY} ist der App-Assemblyname der App (z. B. BlazorSample).The placeholder {APP ASSEMBLY} is the app's app assembly name (for example, BlazorSample).

Pages/JSInteropComponent.razor:Pages/JSInteropComponent.razor:

@page "/JSInteropComponent"

<p>
    Message: @message
</p>

<p>
    <button onclick="updateMessageCallerJS()">Call JS Method</button>
</p>

@code {
    private static Action action;
    private string message = "Select the button.";

    protected override void OnInitialized()
    {
        action = UpdateMessage;
    }

    private void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        StateHasChanged();
    }

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

Wenn mehrere Komponenten mit eigenen aufzurufenden Instanzmethoden vorliegen, verwenden Sie eine Hilfsklasse, um die Instanzmethoden aller Komponenten (als Action) aufzurufen.When there are several components, each with instance methods to call, use a helper class to invoke the instance methods (as Actions) of each component.

Im folgenden Beispiel:In the following example:

  • Die JSInteropExample-Komponente enthält mehrere ListItem-Komponenten.The JSInteropExample component contains several ListItem components.
  • Alle ListItem-Komponenten bestehen aus einer Nachricht und einer Schaltfläche.Each ListItem component is composed of a message and a button.
  • Wenn auf die Schaltfläche einer ListItem-Komponente geklickt wird, ändert die UpdateMessage-Methode des ListItem-Elements den Text des Listenelements und blendet die Schaltfläche aus.When a ListItem component button is selected, that ListItem's UpdateMessage method changes the list item text and hides the button.

MessageUpdateInvokeHelper.cs:MessageUpdateInvokeHelper.cs:

using System;
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    private Action action;

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

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

Der Platzhalter {APP ASSEMBLY} ist der App-Assemblyname der App (z. B. BlazorSample).The placeholder {APP ASSEMBLY} is the app's app assembly name (for example, BlazorSample).

In der clientseitigen JavaScript-Datei:In the client-side JavaScript:

window.updateMessageCallerJS = (dotnetHelper) => {
    dotnetHelper.invokeMethod('{APP ASSEMBLY}', 'UpdateMessageCaller');
    dotnetHelper.dispose();
}

Shared/ListItem.razor:Shared/ListItem.razor:

@inject IJSRuntime JsRuntime

<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 JsRuntime.InvokeVoidAsync("updateMessageCallerJS",
            DotNetObjectReference.Create(messageUpdateInvokeHelper));
    }

    private void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }
}

Pages/JSInteropExample.razor:Pages/JSInteropExample.razor:

@page "/JSInteropExample"

<h1>List of components</h1>

<ul>
    <ListItem />
    <ListItem />
    <ListItem />
    <ListItem />
</ul>

Freigeben von Interop-Code in einer KlassenbibliothekShare interop code in a class library

JavaScript-Interop-Code kann in einer Klassenbibliothek eingeschlossen werden, wodurch Sie den Code in einem NuGet-Paket freigeben können.JS interop code can be included in a class library, which allows you to share the code in a NuGet package.

Die Klassenbibliothek behandelt das Einbetten von JavaScript-Ressourcen in der erstellten Assembly.The class library handles embedding JavaScript resources in the built assembly. Die JavaScript-Dateien werden im Ordner wwwroot gespeichert.The JavaScript files are placed in the wwwroot folder. Die Tools kümmern sich um das Einbetten der Ressourcen, wenn die Bibliothek erstellt wird.The tooling takes care of embedding the resources when the library is built.

In der Projektdatei der App wird so auf das NuGet-Paket verwiesen, wie auf jedes andere NuGet-Paket verwiesen wird.The built NuGet package is referenced in the app's project file the same way that any NuGet package is referenced. Nachdem das Paket wiederhergestellt wurde, kann App-Code einen Aufruf in JavaScript wie auch unter C# durchführen.After the package is restored, app code can call into JavaScript as if it were C#.

Weitere Informationen finden Sie unter Klassenbibliotheken für ASP.NET Core-Razor-Komponenten.For more information, see Klassenbibliotheken für ASP.NET Core-Razor-Komponenten.

Vermeiden von ObjektzirkelbezügenAvoid circular object references

Objekte, die Zirkelbezüge enthalten, können auf dem Client für folgende Vorgänge nicht serialisiert werden:Objects that contain circular references can't be serialized on the client for either:

  • .NET-Methodenaufrufe..NET method calls.
  • JavaScript-Methodenaufrufe von C#, wenn der Rückgabetyp Zirkelbezüge enthält.JavaScript method calls from C# when the return type has circular references.

Weitere Informationen finden Sie unter den folgenden Problemen:For more information, see the following issues:

Zusätzliche RessourcenAdditional resources