Dela via


Anropa .NET-metoder från JavaScript-funktioner i ASP.NET Core Blazor

Anmärkning

Det här är inte den senaste versionen av den här artikeln. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Varning

Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i supportpolicyn för .NET och .NET Core. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Viktigt!

Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.

För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Den här artikeln beskriver hur du anropar .NET-metoder från JavaScript (JS).

Information om hur du anropar JS funktioner från .NET finns i Anropa JavaScript-funktioner från .NET-metoder i ASP.NET Core Blazor.

Anropa en statisk .NET-metod

Om du vill anropa en statisk .NET-metod från JavaScript (JS) använder du JS funktionerna:

  • DotNet.invokeMethodAsync (rekommenderas): Asynkron för både komponenter på serversidan och på klientsidan.
  • DotNet.invokeMethod: Synkron endast för komponenter på klientsidan.

Skicka in namnet på sammansättningen som innehåller metoden, identifieraren för den statiska .NET-metoden och eventuella argument.

I följande exempel:

  • Platshållaren {PACKAGE ID/ASSEMBLY NAME} är projektets paket-ID (<PackageId> i projektfilen) för ett bibliotek eller sammansättningsnamn för en app.
  • Platshållaren {.NET METHOD ID} är .NET-metodidentifieraren.
  • Platshållaren {ARGUMENTS} är valfria kommaavgränsade argument för att skicka till metoden, som var och en måste vara JSON-serialiserbara.
DotNet.invokeMethodAsync('{PACKAGE ID/ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});

DotNet.invokeMethodAsync returnerar ett JS Promise som representerar resultatet av åtgärden. DotNet.invokeMethod (komponenter på klientsidan) returnerar resultatet av åtgärden.

Viktigt!

För komponenter på serversidan rekommenderar vi den asynkrona funktionen (invokeMethodAsync) över den synkrona versionen (invokeMethod).

.NET-metoden måste vara offentlig, statisk och ha attributet[JSInvokable] .

I följande exempel:

  • Platshållaren {<T>} anger returtypen, som endast krävs för metoder som returnerar ett värde.
  • Platshållaren {.NET METHOD ID} är metodidentifieraren.
@code {
    [JSInvokable]
    public static Task{<T>} {.NET METHOD ID}()
    {
        ...
    }
}

Anmärkning

Anropa öppna allmänna metoder stöds inte med statiska .NET-metoder, men stöds med instansmetoder. Mer information finns i avsnittet Anropa generiska klassmetoder för .NET .

I följande komponent ReturnArrayAsync returnerar C#-metoden en int matris. Attributet[JSInvokable] tillämpas på metoden, vilket gör att metoden kan anropas av 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);
}

Funktionen addHandlersJS lägger till en click händelse på knappen. Funktionen returnArrayAsyncJS tilldelas som hanterare.

Funktionen returnArrayAsyncJS anropar ReturnArrayAsync .NET-metoden för komponenten, som loggar resultatet till webbläsarens webbutvecklarverktygskonsol. BlazorSample är appens sammansättningsnamn.

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

Funktionen addHandlersJS lägger till en click händelse på knappen. Funktionen returnArrayAsyncJS tilldelas som hanterare.

Funktionen returnArrayAsyncJS anropar ReturnArrayAsync .NET-metoden för komponenten, som loggar resultatet till webbläsarens webbutvecklarverktygskonsol. BlazorSample är appens sammansättningsnamn.

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

Elementets <button>onclick HTML-attribut är JavaScripts onclick händelsehanterartilldelning för bearbetning av click händelser, inte @onclickBlazordirektivattribut. Funktionen returnArrayAsyncJS tilldelas som hanterare.

returnArrayAsync JS Följande funktion anropar ReturnArrayAsync .NET-metoden för komponenten, som loggar resultatet till webbläsarens webbutvecklarverktygskonsol. BlazorSample är appens sammansättningsnamn.

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

Anmärkning

Allmän vägledning om JS plats och våra rekommendationer för produktionsappar finns i JavaScript-plats i ASP.NET Core Blazor-appar.

När knappen Trigger .NET static method väljs visar webbläsarens utvecklarverktygens konsol matrisdata. Utdataformatet skiljer sig något mellan webbläsare. Följande utdata visar det format som används av Microsoft Edge:

Array(3) [ 11, 12, 13 ]

Skicka data till en .NET-metod när du invokeMethodAsync anropar funktionen genom att skicka data som argument.

För att demonstrera överföring av data till .NET skickar du en startposition till metoden ReturnArrayAsync där metoden anropas i 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>

Komponentens anropande ReturnArrayAsync metod tar emot startpositionen och konstruerar matrisen från den. Matrisen returneras för loggning till konsolen:

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

När appen har omkompilerats och webbläsaren har uppdaterats visas följande utdata i webbläsarens konsol när knappen är markerad:

Array(3) [ 14, 15, 16 ]

.NET-metodidentifieraren för anropet JS är .NET-metodnamnet, men du kan ange en annan identifierare med hjälp av [JSInvokable] attributkonstruktorn . I följande exempel DifferentMethodName är den tilldelade metodidentifieraren för ReturnArrayAsync metoden:

[JSInvokable("DifferentMethodName")]

I anropet till DotNet.invokeMethodAsync (komponenter på serversidan eller klientsidan) eller DotNet.invokeMethod (endast komponenter på klientsidan) anropar du DifferentMethodName för att köra ReturnArrayAsync .NET-metoden:

  • DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
  • DotNet.invokeMethod('BlazorSample', 'DifferentMethodName'); (endast komponenter på klientsidan)

Anmärkning

Metodexemplet ReturnArrayAsync i det här avsnittet returnerar resultatet av ett Task utan att använda explicitA C# async och await nyckelord. Kodningsmetoder med async och await är typiska för metoder som använder nyckelordet await för att returnera värdet för asynkrona åtgärder.

ReturnArrayAsync -metod som består av async och await nyckelord:

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

För mer information, se Asynkron programmering med async och await i C#-guiden.

Skapa JavaScript-objekt och datareferenser för att skicka till .NET

Anropa DotNet.createJSObjectReference(jsObject) för att skapa en JS objektreferens så att den kan skickas till .NET, där jsObject används JS Object för att skapa objektreferensen JS . I följande exempel skickas en referens till det icke-serialiserbara window objektet till .NET, som tar emot det i ReceiveWindowObject C#-metoden som :IJSObjectReference

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

I föregående exempel {PACKAGE ID/ASSEMBLY NAME} är platshållaren projektets paket-ID (<PackageId> i projektfilen) för ett bibliotek eller sammansättningsnamn för en app.

Anmärkning

Föregående exempel kräver inte bortskaffande av JSObjectReference, eftersom en referens till window objektet inte finns i JS.

För att upprätthålla en referens till en JSObjectReference krävs det att den avyttras för att undvika läckage av JS-minne på klienten. Följande exempel omstrukturerar föregående kod för att fånga en referens till JSObjectReference, följt av ett anrop till DotNet.disposeJSObjectReference() för att ta bort referensen.

var jsObjectReference = DotNet.createJSObjectReference(window);

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

DotNet.disposeJSObjectReference(jsObjectReference);

I föregående exempel {PACKAGE ID/ASSEMBLY NAME} är platshållaren projektets paket-ID (<PackageId> i projektfilen) för ett bibliotek eller sammansättningsnamn för en app.

Anropa DotNet.createJSStreamReference(streamReference) för att skapa en JS strömreferens så att den kan skickas till .NET, där streamReference är en ArrayBuffer, Blob, eller en typad matris, till exempel Uint8Array eller Float32Array, som används för att skapa JS-strömreferensen.

Anropa en .NET-instansmetod

Så här anropar du en .NET-instansmetod från JavaScript (JS):

  • Skicka .NET-instansen med referens till JS genom att omsluta instansen i en DotNetObjectReference och anropa Create på den.

  • Anropa en .NET-instansmetod från JS med invokeMethodAsync (rekommenderas) eller invokeMethod (endast komponenter på klientsidan) från den överförda DotNetObjectReference. Skicka identifieraren för instansens .NET-metod och eventuella argument. .NET-instansen kan också skickas som ett argument när du anropar andra .NET-metoder från JS.

    I följande exempel:

    • dotNetHelper är en DotNetObjectReference.
    • Platshållaren {.NET METHOD ID} är .NET-metodidentifieraren.
    • Platshållaren {ARGUMENTS} är valfria kommaavgränsade argument för att skicka till metoden, som var och en måste vara JSON-serialiserbara.
    dotNetHelper.invokeMethodAsync('{.NET METHOD ID}', {ARGUMENTS});
    

    Anmärkning

    invokeMethodAsync och invokeMethod acceptera inte en parameter för sammansättningsnamn när du anropar en instansmetod.

    invokeMethodAsync returnerar ett JS Promise som representerar resultatet av åtgärden. invokeMethod (endast komponenter på klientsidan) returnerar resultatet av åtgärden.

    Viktigt!

    För komponenter på serversidan rekommenderar vi den asynkrona funktionen (invokeMethodAsync) över den synkrona versionen (invokeMethod).

  • Ta bort DotNetObjectReference.

Följande avsnitt i den här artikeln visar olika metoder för att anropa en .NET-instansmetod:

Undvik att trimma JavaScript-anropande .NET-metoder

Det här avsnittet gäller för appar på klientsidan med AOT-kompilering (ahead-of-time) och omlänkning vid körning aktiverat.

Flera av exemplen i följande avsnitt baseras på en klassinstansmetod, där den JavaScript-anropande .NET-metoden som är markerad med [JSInvokable] attributet är medlem i en klass som inte är en Razor komponent. När sådana .NET-metoder finns i en Razor komponent skyddas de från runtime-omlänkning/trimning. För att skydda .NET-metoderna från att trimma utanför Razor komponenter implementerar du metoderna med DynamicDependency attributet på klassens konstruktor, vilket visas i följande exempel:

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

public class ExampleClass {

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

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

Mer information finns i Förbereda .NET-bibliotek för trimning: DynamicDependency.

Skicka en DotNetObjectReference till en enskild JavaScript-funktion

Exemplet i det här avsnittet visar hur du skickar en DotNetObjectReference till en enskild JavaScript-funktion (JS).

Följande sayHello1JS funktion tar emot en DotNetObjectReference och anropar invokeMethodAsync för att anropa GetHelloMessage .NET-metoden för en komponent:

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

Anmärkning

Allmän vägledning om JS plats och våra rekommendationer för produktionsappar finns i JavaScript-plats i ASP.NET Core Blazor-appar.

I föregående exempel är variabelnamnet dotNetHelper godtyckligt och kan ändras till valfritt önskat namn.

För följande komponent:

  • Komponenten har en JS-invokable .NET-metod med namnet GetHelloMessage.
  • Trigger .NET instance method När knappen har valts JS anropas funktionen sayHello1 med DotNetObjectReference.
  • sayHello1:
    • Anropar GetHelloMessage och tar emot meddelanderesultatet.
    • Returnerar meddelanderesultatet till anropande TriggerDotNetInstanceMethod metod.
  • Det returnerade meddelandet inifrån sayHello1result visas för användaren.
  • För att undvika en minnesläcka och tillåta skräpinsamling tas .NET-objektreferensen som skapats av DotNetObjectReference bort i Dispose -metoden.

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

I föregående exempel är variabelnamnet dotNetHelper godtyckligt och kan ändras till valfritt önskat namn.

Använd följande vägledning för att skicka argument till en instansmetod:

Lägg till parametrar i .NET-metodens anrop. I följande exempel skickas ett namn till metoden. Lägg till ytterligare parametrar i listan efter behov.

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

I föregående exempel är variabelnamnet dotNetHelper godtyckligt och kan ändras till valfritt önskat namn.

Ange parameterlistan till .NET-metoden.

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

I föregående exempel är variabelnamnet dotNetHelper godtyckligt och kan ändras till valfritt önskat namn.

Skicka en DotNetObjectReference till en klass med flera JavaScript-funktioner

Exemplet i det här avsnittet visar hur du skickar en DotNetObjectReference till en JavaScript-klass (JS) med flera funktioner.

Skapa och skicka en DotNetObjectReference från OnAfterRenderAsync livscykelmetoden till en JS klass för att flera funktioner ska kunna använda den. Kontrollera att .NET-koden gör sig av med DotNetObjectReference, som visas i följande exempel.

I följande komponent anropar knapparna funktioner genom att ange JSonclick-egenskapen, inte Blazor@onclick-direktivattributet.

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

I föregående exempel:

  • JS är en inmatad IJSRuntime instans. IJSRuntime registreras av Blazor ramverket.
  • Variabelnamnet dotNetHelper är godtyckligt och kan ändras till valfritt önskat namn.
  • Komponenten måste uttryckligen ta bort för DotNetObjectReference att tillåta skräpinsamling och förhindra en minnesläcka.
  • JSDisconnectedException fångas under modulens bortskaffande om Blazors SignalR krets går förlorad. Om föregående kod används i en Blazor WebAssembly app finns det ingen SignalR anslutning att förlora, så du kan ta bort try-catch-blocket och lämna raden som avyttrar modulen (await module.DisposeAsync();). Mer information finns i ASP.NET Core Blazor JavaScript-samverkan (JS interop).

CallDotNetExampleOneHelper.razor.js:

export class GreetingHelpers {
  static dotNetHelper;

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

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

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

export function addHandlers() {
  const sayHelloBtn = document.getElementById("sayHelloBtn");
  sayHelloBtn.addEventListener("click", GreetingHelpers.sayHello);

  const welcomeVisitorBtn = document.getElementById("welcomeVisitorBtn");
  welcomeVisitorBtn.addEventListener("click", GreetingHelpers.welcomeVisitor);
}

I föregående exempel är variabelnamnet dotNetHelper godtyckligt och kan ändras till valfritt önskat namn.

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

I föregående exempel:

  • JS är en inmatad IJSRuntime instans. IJSRuntime registreras av Blazor ramverket.
  • Variabelnamnet dotNetHelper är godtyckligt och kan ändras till valfritt önskat namn.
  • Komponenten måste uttryckligen frigöra DotNetObjectReference för att möjliggöra sophantering och förhindra en minnesläcka.
<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>

I föregående exempel:

  • Klassen GreetingHelpers läggs till i window objektet för att globalt definiera klassen, vilket gör det möjligt Blazor att hitta klassen för JS interop.
  • Variabelnamnet dotNetHelper är godtyckligt och kan ändras till valfritt önskat namn.

Anmärkning

Allmän vägledning om JS plats och våra rekommendationer för produktionsappar finns i JavaScript-plats i ASP.NET Core Blazor-appar.

Anropa generiska klassmetoder för .NET

JavaScript-funktioner (JS) kan anropa .NET generiska klassmetoder , där en JS funktion anropar en .NET-metod för en generisk klass.

I följande generiska typklass (GenericType<TValue>):

  • Klassen har en parameter av en enda typ (TValue) med en enda allmän Value egenskap.
  • Klassen har två icke-generiska metoder markerade med [JSInvokable] attributet, var och en med en generisk typparameter med namnet newValue:
    • Update uppdaterar synkront värdet Value för från newValue.
    • UpdateAsync uppdaterar asynkront värdet av Value från newValue efter att ha skapat en väntbar uppgift med Task.Yield som asynkront återgår till den aktuella kontexten när den inväntas.
  • Klassens metoder skriver typen av TValue och värdet av Value till konsolen. Att skriva till konsolen är endast i demonstrationssyfte. Produktionsappar undviker vanligtvis att skriva till konsolen till förmån för apploggning. Mer information finns i ASP.NET Core-loggning Blazor och loggning i .NET Core och ASP.NET Core.

Anmärkning

Öppna allmänna typer och metoder anger inte typer för typplatshållare. Omvänt levererar slutna generiska typer för alla typplatshållare. Exemplen i det här avsnittet visar stängda generiska objekt, men det stöds att JS anropa interop-instansmetoder med öppna generiska objekt. Användning av öppna generiska objekt stöds inte för statiska .NET-metodanrop, som beskrevs tidigare i den här artikeln.

Mer information finns i följande artiklar:

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

I följande invokeMethodsAsync funktion:

  • Den generiska typklassens Update- och UpdateAsync-metoder anropas med argument som representerar strängar och tal.
  • Komponenter på klientsidan stöder synkront anrop av .NET-metoder med invokeMethod. syncInterop får ett booleskt värde som anger om JS interop inträffar på klienten. När syncInterop är true anropas invokeMethod på ett säkert sätt. Om värdet syncInterop för är falseanropas endast den asynkrona funktionen invokeMethodAsync eftersom JS interop körs i en komponent på serversidan.
  • I demonstrationssyfte skrivs funktionsanropet DotNetObjectReference (invokeMethod eller invokeMethodAsync), .NET-metoden (Update eller UpdateAsync) och argumentet till konsolen. Argumenten använder ett slumptal för att tillåta matchning av JS funktionsanropet till .NET-metodanropet (skrivs också till konsolen på .NET-sidan). Produktionskoden skrivs vanligtvis inte till konsolen, varken på klienten eller servern. Produktionsappar förlitar sig vanligtvis på apploggning. Mer information finns i ASP.NET Core-loggning Blazor och loggning i .NET Core och 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>

Anmärkning

Allmän vägledning om JS plats och våra rekommendationer för produktionsappar finns i JavaScript-plats i ASP.NET Core Blazor-appar.

I den följande GenericsExample-komponenten:

  • Funktionen JSinvokeMethodsAsync anropas när Invoke Interop knappen är markerad.
  • Ett par DotNetObjectReference-typer skapas och skickas till funktionen JS för instanser av GenericType som en string och en 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();
    }
}

I föregående exempel JS är en inmatad IJSRuntime instans. IJSRuntime registreras av Blazor ramverket.

Följande visar typiska utdata från föregående exempel när Invoke Interop knappen väljs i en komponent på klientsidan:

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

Om föregående exempel implementeras i en komponent på serversidan undviks de synkrona anropen med invokeMethod . För komponenter på serversidan rekommenderar vi den asynkrona funktionen (invokeMethodAsync) över den synkrona versionen (invokeMethod).

Typiska utdata för en komponent på serversidan:

JS: invokeMethodAsync:Update('string 34809')
.NET: Uppdatering: GenericTyp<System.String>: sträng 34809
JS: invokeMethodAsync:UpdateAsync('string 93059')
JS: invokeMethodAsync:Update(41997)
.NET: Update: GenericType<System.Int32>: 41997
JS: invokeMethodAsync:UpdateAsync(24652)
.NET: UpdateAsync: GenericType<System.String>: sträng 93059
.NET: UpdateAsync: GenericType<System.Int32>: 24652

Föregående utdataexempel visar att asynkrona metoder körs och slutförs i godtycklig ordning beroende på flera faktorer, inklusive trådschemaläggning och metodkörningshastighet. Det går inte att förutsäga slutförandeordningen för asynkrona metodanrop på ett tillförlitligt sätt.

Exempel på klassinstanser

Följande sayHello1JS funktion:

  • GetHelloMessage Anropar .NET-metoden på den skickade DotNetObjectReference.
  • Returnerar meddelandet från GetHelloMessage till anroparen sayHello1 .
<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

Anmärkning

Allmän vägledning om JS plats och våra rekommendationer för produktionsappar finns i JavaScript-plats i ASP.NET Core Blazor-appar.

I föregående exempel är variabelnamnet dotNetHelper godtyckligt och kan ändras till valfritt önskat namn.

Följande HelloHelper klass har en JS-invokable .NET-metod med namnet GetHelloMessage. När HelloHelper skapas används namnet i Name egenskapen för att returnera ett meddelande från 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}!";
}

Metoden CallHelloHelperGetHelloMessage i följande JsInteropClasses3 klass anropar JS funktionen sayHello1 med en ny instans av 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);
    }
}

För att undvika en minnesläcka och tillåta skräpinsamling tas .NET-objektreferensen som skapas av DotNetObjectReference bort när objektreferensen hamnar utanför omfånget med using var syntax.

Trigger .NET instance method När knappen har valts i följande komponent JsInteropClasses3.CallHelloHelperGetHelloMessage anropas med värdet 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);
    }
}

Följande bild visar den renderade komponenten med namnet Amy Pond i fältet Name . När knappen har valts Hello, Amy Pond! visas den i användargränssnittet:

Renderat komponentexempel för

Föregående mönster som visas i JsInteropClasses3 klassen kan också implementeras helt i en komponent.

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

För att undvika en minnesläcka och tillåta skräpinsamling tas .NET-objektreferensen som skapas av DotNetObjectReference bort när objektreferensen hamnar utanför omfånget med using var syntax.

Utdata som visas av komponenten är Hello, Amy Pond! när namnet Amy Pond anges i fältet name .

I föregående komponent tas .NET-objektreferensen bort. Om en klass eller komponent inte avyttrar DotNetObjectReference, avyttra den från klienten genom att anropa dispose på den överförda DotNetObjectReference:

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

I föregående exempel:

  • Platshållaren {JS FUNCTION NAME} är JS funktionens namn.
  • Variabelnamnet dotNetHelper är godtyckligt och kan ändras till valfritt önskat namn.
  • Platshållaren {.NET METHOD ID} är .NET-metodidentifieraren.

Komponentinstansens .NET-metodhjälpklass

En hjälpklass kan anropa en .NET-instansmetod som en Action. Hjälpklasser är användbara i scenarier där statiska .NET-metoder inte är tillämpliga:

  • När flera komponenter av samma typ återges på samma sida.
  • I appar på serversidan med flera användare samtidigt med samma komponent.

I följande exempel:

  • Komponenten innehåller flera ListItem1 komponenter.
  • Varje ListItem1 komponent består av ett meddelande och en knapp.
  • När en ListItem1 komponentknapp har valts ListItem1UpdateMessage ändrar metoden listobjektets text och döljer knappen.

MessageUpdateInvokeHelper Följande klass upprätthåller en JS-anropbar .NET-metod, UpdateMessageCaller, för att anropa Action när klassen instansieras.

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

updateMessageCaller JS Följande funktion anropar UpdateMessageCaller .NET-metoden.

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

Anmärkning

Allmän vägledning om JS plats och våra rekommendationer för produktionsappar finns i JavaScript-plats i ASP.NET Core Blazor-appar.

I föregående exempel är variabelnamnet dotNetHelper godtyckligt och kan ändras till valfritt önskat namn.

Följande ListItem1 komponent är en delad komponent som kan användas valfritt antal gånger i en överordnad komponent och skapar listobjekt (<li>...</li>) för en HTML-lista (<ul>...</ul> eller <ol>...</ol>). Varje ListItem1-komponentinstans etablerar en instans av MessageUpdateInvokeHelper med Action som är satt till sin UpdateMessage-metod.

När en ListItem1-komponents InteropCall-knapp har valts, anropas updateMessageCaller med en skapad DotNetObjectReference för MessageUpdateInvokeHelper-instansen. Detta gör det möjligt för ramverket att anropa UpdateMessageCallerListItem1s MessageUpdateInvokeHelper instans. Det överlämnade DotNetObjectReference kasseras i 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 anropas för att uppdatera användargränssnittet när message har angetts i UpdateMessage. Om StateHasChanged inte anropas har Blazor ingen möjlighet att veta att användargränssnittet ska uppdateras när Action anropas.

Följande överordnade komponent innehåller fyra listobjekt, var och en en instans av komponenten 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>

Följande bild visar den renderade överordnade komponenten när den andra InteropCall knappen har valts:

  • Den andra ListItem1 komponenten har visat meddelandet UpdateMessage Called! .
  • Knappen InteropCall för den andra ListItem1 komponenten visas inte eftersom knappens CSS-egenskap display är inställd på none.

Renderat komponentexempel för

Komponentinstansens .NET-metod anropad från DotNetObjectReference tilldelad till en elementegenskap

Tilldelningen av en DotNetObjectReference till en egenskap för ett HTML-element tillåter anrop av .NET-metoder på en komponentinstans:

På samma sätt som den metod som beskrivs i avsnittet komponentinstans .NET-metodhjälpklass är den här metoden användbar i scenarier där användning av statiska .NET-metoder inte är tillämpliga:

  • När flera komponenter av samma typ återges på samma sida.
  • I appar på serversidan med flera användare samtidigt med samma komponent.
  • .NET-metoden anropas från en JS händelse (till exempel onclick), inte från en Blazor händelse (till exempel @onclick).

I följande exempel:

  • Komponenten innehåller flera ListItem2 komponenter, som är en delad komponent.
  • Varje ListItem2 komponent består av ett listobjektmeddelande <span> och en sekund <span> med en display CSS-egenskap inställd på inline-block för visning.
  • När ett ListItem2 komponentelement i listan är markerat, använder ListItem2UpdateMessage metoden för att ändra den angivna listobjektets text i den första <span> och döljer den andra <span> genom att ställa in dess display egenskap till none.

Följande assignDotNetHelperJS funktion tilldelar DotNetObjectReference till ett element i en egenskap med namnet dotNetHelper. Följande interopCallJS funktion använder DotNetObjectReference för det skickade elementet för att anropa en .NET-metod med namnet 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>

Anmärkning

Allmän vägledning om JS plats och våra rekommendationer för produktionsappar finns i JavaScript-plats i ASP.NET Core Blazor-appar.

I föregående exempel är variabelnamnet dotNetHelper godtyckligt och kan ändras till valfritt önskat namn.

Följande ListItem2 komponent är en delad komponent som kan användas valfritt antal gånger i en överordnad komponent och skapar listobjekt (<li>...</li>) för en HTML-lista (<ul>...</ul> eller <ol>...</ol>).

Varje ListItem2 komponentinstans anropar assignDotNetHelperJS funktionen i OnAfterRenderAsync med en elementreferens (det första <span> elementet i listobjektet) och komponentinstansen som en DotNetObjectReference.

När meddelandet från en ListItem2-komponent <span> har valts, anropas interopCall och elementet <span> skickas som en parameter (this), vilket i sin tur anropar .NET-metoden UpdateMessage. I UpdateMessage anropas StateHasChanged för att uppdatera användargränssnittet när message har ställts in och när display-egenskapen för den andra <span> uppdateras. Om StateHasChanged inte anropas har Blazor ingen möjlighet att veta att användargränssnittet ska uppdateras när metoden anropas.

DotNetObjectReference Tas bort när komponenten tas bort.

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

Följande överordnade komponent innehåller fyra listobjekt, var och en en instans av komponenten 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>

Synkrona JS-interoperabilitet i klientkomponenter

Det här avsnittet gäller endast komponenter på klientsidan.

JS interop-anrop är asynkrona, oavsett om den anropade koden är synkron eller asynkron. Anrop är asynkrona för att säkerställa att komponenterna är kompatibla i återgivningslägen på serversidan och på klientsidan. På servern måste alla JS interop-anrop vara asynkrona eftersom de skickas via en nätverksanslutning.

Om du vet att komponenten bara körs på WebAssembly kan du välja att göra synkrona JS interop-anrop. Detta har något mindre omkostnader än att göra asynkrona anrop och kan resultera i färre återgivningscykler eftersom det inte finns något mellanliggande tillstånd i väntan på resultat.

Om du vill göra ett synkront anrop från JavaScript till .NET i en komponent på klientsidan använder du DotNet.invokeMethod i stället för DotNet.invokeMethodAsync.

Synkrona anrop fungerar om:

  • Komponenten utfördes endast på WebAssembly.
  • Den anropade funktionen returnerar ett värde synkront. Funktionen är inte en async metod och returnerar inte .NET Task eller JavaScript Promise.

JavaScript-plats

Läs in JavaScript-kod (JS) med någon av de metoder som beskrivs i -artikeln om JavaScript-plats:

Använd JS-moduler för att läsa in JS vilket beskrivs i artikelns avsnitt JavaScript-isolering i JavaScript-moduler.

Varning

Placera bara en <script>-tagg i en komponentfil (.razor) om komponenten garanterat använder statisk återgivning på serversidan (statisk SSR) eftersom taggen <script> inte kan uppdateras dynamiskt.

Varning

Placera inte en <script>-tagg i en komponentfil (.razor) eftersom taggen <script> inte kan uppdateras dynamiskt.

JavaScript-isolering i JavaScript-moduler

Blazor aktiverar JavaScript-isolering (JS) i JavaScript-standardmoduler (ECMAScript-specifikation). JavaScript-modulinläsning fungerar på samma sätt i Blazor som för andra typer av webbappar, och du kan anpassa hur moduler definieras i din app. En guide om hur du använder JavaScript-moduler finns i MDN Web Docs: JavaScript-moduler.

JS isolering ger följande fördelar:

  • Importerade JS förorenar inte längre det globala namnområdet.
  • Användare av ett bibliotek och dess komponenter behöver inte importera den relaterade JS.

Mer information finns i Anropa JavaScript-funktioner från .NET-metoder i ASP.NET Core Blazor.

Dynamisk import med operatorn import() stöds med ASP.NET Core och Blazor:

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

I föregående exempel representerar platshållaren {CONDITION} en villkorlig kontroll för att avgöra om modulen ska laddas.

Information om webbläsarkompatibilitet finns i Kan jag använda: JavaScript-moduler: dynamisk import.

Undvik cirkelobjektreferenser

Objekt som innehåller cirkelreferenser kan inte serialiseras på klienten för något av följande:

  • .NET-metodanrop.
  • JavaScript-metoden anropar från C# när returtypen har cirkelreferenser.

Stöd för bytematris

Blazor stöder optimerad bytematris JavaScript (JS) interop som undviker kodning/avkodning av bytematriser till Base64. I följande exempel används JS interop för att skicka en bytematris till .NET.

Ange en sendByteArrayJS funktion. Funktionen kallas statiskt, med parametern för sammansättningsnamnet i invokeMethodAsync-anropet, via en knapp i komponenten, och returnerar inget värde.

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>

Anmärkning

Allmän vägledning om JS plats och våra rekommendationer för produktionsappar finns i JavaScript-plats i ASP.NET Core Blazor-appar.

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

Information om hur du använder en bytematris när du anropar JavaScript från .NET finns i Anropa JavaScript-funktioner från .NET-metoder i ASP.NET Core Blazor.

Strömma från JavaScript till .NET

Blazor stöder direktuppspelning av data från JavaScript till .NET. Strömmar begärs med hjälp av Microsoft.JSInterop.IJSStreamReference gränssnittet.

Microsoft.JSInterop.IJSStreamReference.OpenReadStreamAsync returnerar en Stream och använder följande parametrar:

  • maxAllowedSize: Maximalt antal byte som tillåts för läsåtgärden från JavaScript, som standard är 512 000 byte om det inte anges.
  • cancellationToken: A CancellationToken för att avbryta läsningen.

I JavaScript:

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

I C#-kod:

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

I föregående exempel:

  • JS är en inmatad IJSRuntime instans. IJSRuntime registreras av Blazor ramverket.
  • dataReferenceStream Skrivs till disken (file.txt) på den aktuella användarens temporära mappsökväg (GetTempPath).

Anropa JavaScript-funktioner från .NET-metoder i ASP.NET Core Blazor omfattar den omvända åtgärden, direktuppspelning från .NET till JavaScript med hjälp av en DotNetStreamReference.

ASP.NET Core-filuppladdningar Blazor beskriver hur du laddar upp en fil i Blazor. Ett formulärexempel som strömmar <textarea> data i en komponent på serversidan finns i Felsöka ASP.NET Core-formulärBlazor.

JavaScript-interop [JSImport]/[JSExport]

Det här avsnittet gäller komponenter på klientsidan.

Som ett alternativ till att interagera med JavaScript (JS) i komponenter på klientsidan med hjälp av BlazorJS interop-mekanism baserat på IJSRuntime-gränssnittet är ett JS[JSImport]/[JSExport] interop-API tillgängligt för appar som riktar sig till .NET 7 eller senare.

Mer information finns i JavaScript JSImport/JSExport interop med ASP.NET Core Blazor.

Borttagning av javascript-interopobjektreferenser

Exempel i interop-artiklarna i JavaScript (JS) visar typiska mönster för bortskaffande av objekt:

JS interop-objektreferenser implementeras som en karta där nyckeln är en identifierare på sidan av JS interop-anropet som skapar referensen. När bortskaffande av objekt initieras från antingen .NET- eller JS-sidan tar Blazor bort posten från kartan, och objektet kan bli föremål för avfallshantering så länge det inte finns någon annan stark referens till objektet.

Ta alltid bort objekt som skapats på .NET-sidan för att undvika att läcka .NET-hanterat minne.

DOM-rensningsuppgifter vid bortskaffande av komponenter

Mer information finns i ASP.NET Core Blazor JavaScript-samverkan (JS interop).

JavaScript-interopanrop utan krets

Mer information finns i ASP.NET Core Blazor JavaScript-samverkan (JS interop).

Ytterligare resurser