Sdílet prostřednictvím


Volání metod .NET z funkcí JavaScriptu v ASP.NET Core Blazor

Poznámka:

Toto není nejnovější verze tohoto článku. Aktuální verzi najdete v tomto článku ve verzi .NET 9.

Upozorňující

Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v zásadách podpory .NET a .NET Core. Aktuální verzi najdete v tomto článku ve verzi .NET 9.

Důležité

Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.

Aktuální verzi najdete v tomto článku ve verzi .NET 9.

Tento článek vysvětluje, jak vyvolat metody .NET z JavaScriptu (JS).

Informace o volání JS funkcí z .NET naleznete v tématu Volání javascriptových funkcí z metod .NET v ASP.NET Core Blazor.

Vyvolání statické metody .NET

Pokud chcete vyvolat statickou metodu .NET z JavaScriptu (JS), použijte JS tyto funkce:

  • DotNet.invokeMethodAsync (doporučeno): Asynchronní pro součásti na straně serveru i na straně klienta.
  • DotNet.invokeMethod: Synchronní pouze pro komponenty na straně klienta.

Předejte název sestavení obsahující metodu, identifikátor statické metody .NET a všechny argumenty.

V následujícím příkladu:

  • Zástupný {ASSEMBLY NAME} symbol je název sestavení aplikace.
  • Zástupný {.NET METHOD ID} symbol je identifikátor metody .NET.
  • Zástupný {ARGUMENTS} symbol je volitelný argument oddělený čárkami, které se mají předat metodě, z nichž každý musí být serializovatelný ve formátu JSON.
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});

DotNet.invokeMethodAsyncJS Promise vrátí reprezentaci výsledku operace. DotNet.invokeMethod (komponenty na straně klienta) vrátí výsledek operace.

Důležité

U komponent na straně serveru doporučujeme asynchronní funkci (invokeMethodAsync) nad synchronní verzí (invokeMethod).

Metoda .NET musí být veřejná, statická a musí mít [JSInvokable] atribut.

V následujícím příkladu:

  • Zástupný {<T>} symbol označuje návratový typ, který se vyžaduje pouze pro metody, které vracejí hodnotu.
  • Zástupný {.NET METHOD ID} symbol je identifikátor metody.
@code {
    [JSInvokable]
    public static Task{<T>} {.NET METHOD ID}()
    {
        ...
    }
}

Poznámka:

Volání otevřených obecných metod není podporováno u statických metod .NET, ale podporuje se metodami instance. Další informace naleznete v části Volání metod obecné třídy .NET.

V následující komponentě ReturnArrayAsync vrátí int metoda C# pole. Atribut [JSInvokable] se použije na metodu, která způsobí, že metoda vyvolá .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);
}

Funkce addHandlersJS přidá click do tlačítka událost. Funkce returnArrayAsyncJS je přiřazena jako obslužná rutina.

Funkce returnArrayAsyncJS volá metodu ReturnArrayAsync .NET komponenty, která zaprotokoluje výsledek do konzoly webových vývojářských nástrojů prohlížeče. BlazorSample je název sestavení aplikace.

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

Funkce addHandlersJS přidá click do tlačítka událost. Funkce returnArrayAsyncJS je přiřazena jako obslužná rutina.

Funkce returnArrayAsyncJS volá metodu ReturnArrayAsync .NET komponenty, která zaprotokoluje výsledek do konzoly webových vývojářských nástrojů prohlížeče. BlazorSample je název sestavení aplikace.

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

Atribut <button> HTML elementu onclick je přiřazení obslužné rutiny události JavaScriptu onclick pro zpracování click událostí, nikoli Blazoratribut direktivy @onclick . Funkce returnArrayAsyncJS je přiřazena jako obslužná rutina.

Následující returnArrayAsyncJS funkce volá metodu ReturnArrayAsync .NET komponenty, která zaprotokoluje výsledek do konzoly webových vývojářských nástrojů prohlížeče. BlazorSample je název sestavení aplikace.

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

Poznámka:

Obecné pokyny k JS umístění a doporučení pro produkční aplikace najdete v tématu o umístění JavaScriptu v aplikacích ASP.NET CoreBlazor.

Trigger .NET static method Po výběru tlačítka zobrazí výstup konzoly vývojářských nástrojů prohlížeče data pole. Formát výstupu se mírně liší mezi prohlížeči. Následující výstup ukazuje formát používaný aplikací Microsoft Edge:

Array(3) [ 11, 12, 13 ]

Při volání invokeMethodAsync funkce předejte data do metody .NET předáním dat jako argumentů.

Chcete-li předvést předávání dat do rozhraní .NET, předejte počáteční pozici ReturnArrayAsync metodě, ve které je metoda vyvolána: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>

Vyvolání ReturnArrayAsync metody komponenty obdrží počáteční pozici a vytvoří z ní pole. Pole se vrátí pro protokolování do konzoly:

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

Po opětovném zkompilování aplikace a aktualizaci prohlížeče se po výběru tlačítka zobrazí v konzole prohlížeče následující výstup:

Array(3) [ 14, 15, 16 ]

Identifikátor metody .NET pro JS volání je název metody .NET, ale pomocí konstruktoru [JSInvokable] atributů můžete zadat jiný identifikátor. V následujícím příkladu DifferentMethodName je přiřazený identifikátor metody pro metodu ReturnArrayAsync :

[JSInvokable("DifferentMethodName")]

Při volání DotNet.invokeMethodAsync (součásti na straně serveru nebo na straně klienta) nebo DotNet.invokeMethod (pouze komponenty na straně klienta) volání DifferentMethodName metody ReturnArrayAsync .NET:

  • DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
  • DotNet.invokeMethod('BlazorSample', 'DifferentMethodName'); (pouze komponenty na straně klienta)

Poznámka:

Příklad ReturnArrayAsync metody v této části vrátí výsledek Task bez použití explicitního jazyka C# async a await klíčových slov. Metody kódování a async await jsou typické metody, které používají await klíčové slovo k vrácení hodnoty asynchronních operací.

ReturnArrayAsync metoda složená s async klíčovými await slovy:

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

Další informace naleznete v tématu Asynchronní programování pomocí async a await v průvodci jazykem C#.

Vytvoření odkazů na javascriptový objekt a data pro předání do .NET

Volání DotNet.createJSObjectReference(jsObject) k JS vytvoření odkazu na objekt, aby bylo možné jej předat do .NET, kde jsObject se JS Object používá k vytvoření JS odkazu na objekt. Následující příklad předá odkaz na objekt, který není serializovatelný window objekt .NET, který ho přijímá v ReceiveWindowObject metodě C# jako IJSObjectReference:

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

V předchozím příkladu {ASSEMBLY NAME} je zástupný symbol obor názvů aplikace.

Poznámka:

Předchozí příklad nevyžaduje odstranění objektu JSObjectReference, jako odkaz na window objekt není uložen .JS

Udržování odkazu na objekt JSObjectReference vyžaduje, aby se zabránilo nevrácení JS paměti v klientovi. Následující příklad refaktoruje předchozí kód, aby zachytil odkaz na JSObjectReference, následovaný voláním DotNet.disposeJSObjectReference() pro odstranění odkazu:

var jsObjectReference = DotNet.createJSObjectReference(window);

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

DotNet.disposeJSObjectReference(jsObjectReference);

V předchozím příkladu {ASSEMBLY NAME} je zástupný symbol obor názvů aplikace.

Volání DotNet.createJSStreamReference(streamReference) k JS vytvoření odkazu na datový proud tak, aby bylo možné jej předat do .NET, kde streamReference je ArrayBuffer, Blobnebo jakékoli typové pole, například Uint8Array nebo Float32Array, použité k vytvoření JS odkazu na datový proud.

Vyvolání metody .NET instance

Vyvolání metody .NET instance z JavaScriptu (JS):

  • Předejte instanci .NET odkazem tak, že JS zabalíte instanci do DotNetObjectReference a zavoláte Create na ni.

  • Vyvolání metody instance .NET pomocí JS (invokeMethodAsyncdoporučeno) nebo invokeMethod (pouze komponenty na straně klienta) z předaného DotNetObjectReference. Předejte identifikátor metody .NET instance a všechny argumenty. Instanci .NET lze také předat jako argument při vyvolání jiných metod .NET z JS.

    V následujícím příkladu:

    • dotNetHelperje .DotNetObjectReference
    • Zástupný {.NET METHOD ID} symbol je identifikátor metody .NET.
    • Zástupný {ARGUMENTS} symbol je volitelný argument oddělený čárkami, které se mají předat metodě, z nichž každý musí být serializovatelný ve formátu JSON.
    dotNetHelper.invokeMethodAsync('{.NET METHOD ID}', {ARGUMENTS});
    

    Poznámka:

    invokeMethodAsync a invokeMethod nepřijme parametr názvu sestavení při vyvolání metody instance.

    invokeMethodAsyncJS Promise vrátí reprezentaci výsledku operace. invokeMethod (pouze komponenty na straně klienta) vrátí výsledek operace.

    Důležité

    U komponent na straně serveru doporučujeme asynchronní funkci (invokeMethodAsync) nad synchronní verzí (invokeMethod).

  • Likvidujte DotNetObjectReference.

Následující části tohoto článku ukazují různé přístupy k vyvolání metody .NET instance:

Vyhněte se oříznutí metod javascript-invokable .NET

Tato část se týká aplikací na straně klienta s povolenou kompilací AOT (Head-of-Time) a opětovným propojením za běhu.

Několik příkladů v následujících částech vychází z přístupu instance třídy, kde metoda JavaScript-invokable .NET označená atributem [JSInvokable] je členem třídy, která není Razor součástí. Pokud jsou takové metody .NET umístěné v komponentě Razor , jsou chráněné před opětovným propojením a oříznutím za běhu. Aby bylo možné chránit metody .NET před oříznutím mimo Razor komponenty, implementujte metody s atributem DynamicDependency v konstruktoru třídy, jak ukazuje následující příklad:

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

public class ExampleClass {

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

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

Další informace naleznete v tématu Příprava knihoven .NET pro oříznutí: DynamicDependency.

DotNetObjectReference Předání jednotlivé javascriptové funkce

Příklad v této části ukazuje, jak předat DotNetObjectReference individuální funkci JavaScriptu (JS).

Následující sayHello1JS funkce přijímá DotNetObjectReference volání metody .NET komponenty a volání invokeMethodAsync GetHelloMessage :

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

Poznámka:

Obecné pokyny k JS umístění a doporučení pro produkční aplikace najdete v tématu o umístění JavaScriptu v aplikacích ASP.NET CoreBlazor.

V předchozím příkladu je název dotNetHelper proměnné libovolný a lze ho změnit na libovolný preferovaný název.

Pro následující komponentu:

  • Komponenta má metodu JS-invokable .NET s názvem GetHelloMessage.
  • Trigger .NET instance method Když je tlačítko vybráno, JS funkce sayHello1 je volána pomocí DotNetObjectReference.
  • sayHello1:
    • Volání GetHelloMessage a přijetí výsledku zprávy
    • Vrátí výsledek zprávy volající TriggerDotNetInstanceMethod metodě.
  • Vrácená zpráva od sayHello1 uživatele result se zobrazí uživateli.
  • Aby nedocházelo k nevrácení paměti a povolte uvolňování paměti, je v metodě odstraněn odkaz na objekt .NET vytvořený DotNetObjectReference metodou Dispose .

CallDotnet2.razor:

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

<PageTitle>Call .NET 2</PageTitle>

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

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

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

<p>
    @result
</p>

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

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

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

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

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

CallDotnet2.razor:

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

<PageTitle>Call .NET 2</PageTitle>

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

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

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

<p>
    @result
</p>

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

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

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

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

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

CallDotNetExample2.razor:

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

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

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

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

<p>
    @result
</p>

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

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

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

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

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

CallDotNetExample2.razor:

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

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

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

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

<p>
    @result
</p>

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

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

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

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

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

CallDotNetExample2.razor:

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

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

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

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

<p>
    @result
</p>

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

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

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

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

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

CallDotNetExample2.razor:

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

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

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

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

<p>
    @result
</p>

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

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

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

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

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

V předchozím příkladu je název dotNetHelper proměnné libovolný a lze ho změnit na libovolný preferovaný název.

K předání argumentů metodě instance použijte následující doprovodné materiály:

Přidejte parametry do vyvolání metody .NET. V následujícím příkladu se metodě předá název. Podle potřeby přidejte do seznamu další parametry.

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

V předchozím příkladu je název dotNetHelper proměnné libovolný a lze ho změnit na libovolný preferovaný název.

Zadejte seznam parametrů metodě .NET.

CallDotnet3.razor:

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

<PageTitle>Call .NET 3</PageTitle>

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

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

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

<p>
    @result
</p>

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

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

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

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

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

CallDotnet3.razor:

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

<PageTitle>Call .NET 3</PageTitle>

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

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

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

<p>
    @result
</p>

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

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

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

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

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

CallDotNetExample3.razor:

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

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

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

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

<p>
    @result
</p>

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

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

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

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

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

CallDotNetExample3.razor:

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

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

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

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

<p>
    @result
</p>

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

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

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

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

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

CallDotNetExample3.razor:

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

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

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

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

<p>
    @result
</p>

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

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

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

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

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

CallDotNetExample3.razor:

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

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

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

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

<p>
    @result
</p>

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

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

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

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

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

V předchozím příkladu je název dotNetHelper proměnné libovolný a lze ho změnit na libovolný preferovaný název.

DotNetObjectReference Předání třídy s více javascriptovými funkcemi

Příklad v této části ukazuje, jak předat DotNetObjectReference javascriptovou třídu (JS) s více funkcemi.

Vytvořte a předejte DotNetObjectReference z OnAfterRenderAsync metody JS životního cyklu do třídy více funkcí, které se mají použít. Ujistěte se, že kód .NET odstraní DotNetObjectReference, jak ukazuje následující příklad.

V následující komponentě tlačítka volají JS funkce nastavenímonclick JSvlastnosti, nikoli Blazoratributu direktivy@onclick.Trigger JS function

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

V předchozím příkladu:

  • JS je vložená IJSRuntime instance. IJSRuntime je registrován v Blazor rámci.
  • Název dotNetHelper proměnné je libovolný a lze ho změnit na libovolný preferovaný název.
  • Komponenta musí explicitně odstranit povolení uvolňování paměti a zabránit úniku DotNetObjectReference paměti.
  • JSDisconnectedException v případě Blazorztráty okruhu SignalR je zachycen během odstraňování modulu. Pokud se předchozí kód použije v Blazor WebAssembly aplikaci, nedojde ke ztrátě připojeníSignalR, takže můžete odebrat blok a nechat řádek, který modul odstranícatch try-().await module.DisposeAsync(); Další informace najdete v tématu ASP.NET Interoperabilita Core Blazor JavaScriptu (JSinteroperabilita).

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

V předchozím příkladu je název dotNetHelper proměnné libovolný a lze ho změnit na libovolný preferovaný název.

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

V předchozím příkladu:

  • JS je vložená IJSRuntime instance. IJSRuntime je registrován v Blazor rámci.
  • Název dotNetHelper proměnné je libovolný a lze ho změnit na libovolný preferovaný název.
  • Komponenta musí explicitně odstranit povolení uvolňování paměti a zabránit úniku DotNetObjectReference paměti.
<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>

V předchozím příkladu:

  • Třída GreetingHelpers se přidá do objektu window , který globálně definuje třídu, která umožňuje Blazor najít třídu pro JS interoperabilitu.
  • Název dotNetHelper proměnné je libovolný a lze ho změnit na libovolný preferovaný název.

Poznámka:

Obecné pokyny k JS umístění a doporučení pro produkční aplikace najdete v tématu o umístění JavaScriptu v aplikacích ASP.NET CoreBlazor.

Volání metod obecné třídy .NET

Funkce Jazyka JavaScript (JS) mohou volat obecné metody třídy .NET, kde JS funkce volá metodu .NET obecné třídy.

V následující obecné třídě typu (GenericType<TValue>):

  • Třída má jeden parametr typu (TValue) s jedinou obecnou Value vlastností.
  • Třída má dvě negenerické metody označené atributem[JSInvokable], z nichž každý má parametr obecného typu s názvem newValue:
    • Update synchronně aktualizuje hodnotu Value z newValue.
    • UpdateAsync asynchronně aktualizuje hodnotu Value z newValue po vytvoření čekající úlohy s Task.Yield tím, že asynchronně vrátí zpět do aktuálního kontextu při očekávání.
  • Každá z metod třídy zapisuje typ TValue a hodnotu Value do konzoly. Zápis do konzoly je určen pouze pro demonstrační účely. Produkční aplikace se obvykle vyhýbají zápisu do konzoly ve prospěch protokolování aplikací. Další informace najdete v tématu ASP.NET Blazor Protokolování a protokolování v .NET Core a ASP.NET Core.

Poznámka:

Otevřené obecné typy a metody nezadávají typy pro zástupné symboly typů. Naopak zavřené obecné typy poskytují typy pro všechny zástupné symboly typů. Příklady v této části ukazují uzavřené obecné typy, ale podporuje se vyvolání JS metod instance interopu s otevřenými obecnými typy. Použití otevřených obecných typů není podporováno pro vyvolání statických metod .NET, které byly popsány výše v tomto článku.

Další informace najdete v následujících článcích:

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

V následující invokeMethodsAsync funkci:

  • Třídy a UpdateAsync metody obecného typu Update jsou volány s argumenty představujícími řetězce a čísla.
  • Komponenty na straně klienta podporují synchronní volání metod .NET s invokeMethod. syncInterop obdrží logickou hodnotu označující, jestli se v klientovi vyskytuje interoperabilita JS . Kdy syncInterop je true, invokeMethod je bezpečně volána. Pokud je falsehodnota syncInterop , volá se pouze asynchronní funkceinvokeMethodAsync, protože interoperabilita JS se spouští v komponentě na straně serveru.
  • Pro demonstrační účely DotNetObjectReference se volání funkce (invokeMethod nebo invokeMethodAsync), volání metody .NET (Update nebo UpdateAsync) a argument jsou zapsány do konzoly. Argumenty používají náhodné číslo k povolení shody JS volání metody .NET do volání metody .NET (také zapsáno do konzoly na straně .NET). Produkční kód obvykle nezapisuje do konzoly, a to buď v klientovi, nebo na serveru. Produkční aplikace se obvykle spoléhají na protokolování aplikací. Další informace najdete v tématu ASP.NET Blazor Protokolování a protokolování v .NET Core a 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>

Poznámka:

Obecné pokyny k JS umístění a doporučení pro produkční aplikace najdete v tématu o umístění JavaScriptu v aplikacích ASP.NET CoreBlazor.

V následující komponentě GenericsExample:

  • Funkce JS invokeMethodsAsync se volá, když Invoke Interop je tlačítko vybráno.
  • Vytvoří se dvojice DotNetObjectReference typů a předá funkci JS instance GenericType jako string a .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();
    }
}

V předchozím příkladu JS je vložená IJSRuntime instance. IJSRuntime je registrován v Blazor rámci.

Následující příklad ukazuje typický výstup předchozího příkladu, když je tlačítko vybráno Invoke Interop v komponentě na straně klienta:

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

Pokud je předchozí příklad implementován v komponentě na straně serveru, synchronní volání se invokeMethod vyhnete. U komponent na straně serveru doporučujeme asynchronní funkci (invokeMethodAsync) nad synchronní verzí (invokeMethod).

Typický výstup součásti na straně serveru:

JS: invokeMethodAsync:Update('string 34809')
.NET: Aktualizace: GenericType<System.String>: řetězec 34809
JS: invokeMethodAsync:UpdateAsync('string 93059')
JS: invokeMethodAsync:Update(41997)
.NET: Aktualizace: GenericType<System.Int32>: 41997
JS: invokeMethodAsync:UpdateAsync(24652)
.NET: UpdateAsync: GenericType<System.String>: řetězec 93059
.NET: UpdateAsync: GenericType<System.Int32>: 24652

Předchozí příklady výstupu ukazují, že asynchronní metody se provádějí a provádějí v libovolném pořadí v závislosti na několika faktorech, včetně plánování vláken a rychlosti provádění metody. Není možné spolehlivě předpovědět pořadí dokončení pro asynchronní volání metod.

Příklady instancí třídy

sayHello1JS Následující funkce:

  • Volá metodu GetHelloMessage .NET na předaném DotNetObjectReference.
  • Vrátí zprávu od GetHelloMessage volajícího sayHello1 .
<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

Poznámka:

Obecné pokyny k JS umístění a doporučení pro produkční aplikace najdete v tématu o umístění JavaScriptu v aplikacích ASP.NET CoreBlazor.

V předchozím příkladu je název dotNetHelper proměnné libovolný a lze ho změnit na libovolný preferovaný název.

Následující HelloHelper třída má metodu JS-invokable .NET s názvem GetHelloMessage. Při HelloHelper vytváření se název ve Name vlastnosti použije k vrácení zprávy z 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}!";
}

Metoda CallHelloHelperGetHelloMessage v následující JsInteropClasses3 třídě vyvolá JS funkci sayHello1 s novou instancí 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);
    }
}

Aby nedošlo k nevrácení paměti a umožnilo uvolňování paměti, je odkaz na objekt .NET vytvořený DotNetObjectReference odstraněn, když odkaz na objekt přejde mimo rozsah se using var syntaxí.

Trigger .NET instance method Když je tlačítko vybráno v následující komponentě, JsInteropClasses3.CallHelloHelperGetHelloMessage je volána s hodnotou 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);
    }
}

Následující obrázek znázorňuje vykreslovanou komponentu s názvem Amy Pond v Name poli. Po výběru Hello, Amy Pond! tlačítka se zobrazí v uživatelském rozhraní:

Vykreslení příkladu komponenty CallDotNetExample4

Předchozí vzor zobrazený ve JsInteropClasses3 třídě lze také implementovat zcela v 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);
    }
}

Aby nedošlo k nevrácení paměti a umožnilo uvolňování paměti, je odkaz na objekt .NET vytvořený DotNetObjectReference odstraněn, když odkaz na objekt přejde mimo rozsah se using var syntaxí.

Výstup zobrazený komponentou je Hello, Amy Pond! , když Amy Pond je název zadaný v name poli.

V předchozí komponentě je odkaz na objekt .NET uvolněn. Pokud třída nebo komponenta nelikviduje , vyřaďte DotNetObjectReferenceji od klienta voláním dispose předaného DotNetObjectReference:

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

V předchozím příkladu:

  • Zástupný {JS FUNCTION NAME} symbol je JS název funkce.
  • Název dotNetHelper proměnné je libovolný a lze ho změnit na libovolný preferovaný název.
  • Zástupný {.NET METHOD ID} symbol je identifikátor metody .NET.

Pomocná třída metody .NET instance komponenty

Pomocná třída může vyvolat metodu instance .NET jako .Action Pomocné třídy jsou užitečné ve scénářích, kdy použití statických metod .NET neplatí:

  • Pokud se na stejné stránce vykreslí několik součástí stejného typu.
  • V aplikacích na straně serveru s více uživateli současně používajících stejnou komponentu.

V následujícím příkladu:

  • Komponenta obsahuje několik ListItem1 komponent.
  • Každá ListItem1 komponenta se skládá ze zprávy a tlačítka.
  • ListItem1 Když je vybráno tlačítko komponenty, tato ListItem1UpdateMessage metoda změní text položky seznamu a tlačítko skryje.

Následující MessageUpdateInvokeHelper třída udržuje JS-invokable .NET metoda , UpdateMessageCallervyvolat Action zadané při vytvoření instance třídy.

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

Následující updateMessageCallerJS funkce vyvolá metodu UpdateMessageCaller .NET.

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

Poznámka:

Obecné pokyny k JS umístění a doporučení pro produkční aplikace najdete v tématu o umístění JavaScriptu v aplikacích ASP.NET CoreBlazor.

V předchozím příkladu je název dotNetHelper proměnné libovolný a lze ho změnit na libovolný preferovaný název.

Následující ListItem1 komponenta je sdílená komponenta, která se dá použít libovolný početkrát v nadřazené komponentě a vytvoří položky seznamu (<li>...</li>) pro seznam HTML (<ul>...</ul> nebo <ol>...</ol>). Každá ListItem1 instance komponenty vytvoří instanci MessageUpdateInvokeHelper s nastavenou metodou Action UpdateMessage .

ListItem1 Když je vybráno tlačítko komponentyInteropCall, updateMessageCaller vyvolá se s vytvořenou DotNetObjectReference MessageUpdateInvokeHelper instancí. To umožňuje rozhraní volat UpdateMessageCaller ListItem1MessageUpdateInvokeHelper tuto instanci. Předaný DotNetObjectReference se vyřadí do 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 je volána k aktualizaci uživatelského rozhraní, pokud message je nastavena v UpdateMessage. Pokud StateHasChanged není volána, neexistuje způsob, jak zjistit, Blazor že by se uživatelské rozhraní mělo aktualizovat při Action vyvolání.

Následující nadřazená komponenta obsahuje čtyři položky seznamu, každou instanci ListItem1 komponenty.

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>

Následující obrázek znázorňuje vykreslovanou nadřazenou komponentu po výběru druhého InteropCall tlačítka:

  • ListItem1 Druhá komponenta zobrazila UpdateMessage Called! zprávu.
  • Tlačítko InteropCall pro druhou ListItem1 komponentu není viditelné, protože vlastnost CSS display tlačítka je nastavena na none.

Vykreslení příkladu komponenty CallDotNetExample6

Metoda .NET instance komponenty volaná z DotNetObjectReference přiřazené vlastnosti elementu

Přiřazení DotNetObjectReference vlastnosti elementu HTML umožňuje volání metod .NET v instanci komponenty:

Tento přístup je užitečný ve scénářích, kdy použití statických metod .NET není možné použít, podobně jako přístup popsaný v části pomocné třídy metody instance komponenty .NET:

  • Pokud se na stejné stránce vykreslí několik součástí stejného typu.
  • V aplikacích na straně serveru s více uživateli současně používajících stejnou komponentu.
  • Metoda .NET je vyvolána z JS události (například onclick), ne z Blazor události (například @onclick).

V následujícím příkladu:

  • Komponenta obsahuje několik ListItem2 komponent, což je sdílená komponenta.
  • Každá ListItem2 komponenta se skládá ze zprávy <span> položky seznamu a sekundy <span> s vlastností CSS nastavenou display na inline-block zobrazení.
  • ListItem2 Pokud je vybrána položka seznamu součástí, tato ListItem2UpdateMessage metoda změní text položky seznamu v první <span> a skryje sekundu <span> nastavením jeho display vlastnosti na none.

Následující assignDotNetHelperJS funkce přiřadí DotNetObjectReference elementu ve vlastnosti s názvem dotNetHelper. Následující interopCallJS funkce používá DotNetObjectReference pro předaný prvek vyvolá metodu .NET s názvem 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>

Poznámka:

Obecné pokyny k JS umístění a doporučení pro produkční aplikace najdete v tématu o umístění JavaScriptu v aplikacích ASP.NET CoreBlazor.

V předchozím příkladu je název dotNetHelper proměnné libovolný a lze ho změnit na libovolný preferovaný název.

Následující ListItem2 komponenta je sdílená komponenta, která se dá použít libovolný početkrát v nadřazené komponentě a vytvoří položky seznamu (<li>...</li>) pro seznam HTML (<ul>...</ul> nebo <ol>...</ol>).

Každá ListItem2 instance komponenty vyvolá assignDotNetHelperJS funkci OnAfterRenderAsync pomocí odkazu prvku (první <span> prvek položky seznamu) a instance komponenty jako DotNetObjectReference.

ListItem2 Když je vybrána zpráva <span> komponenty, interopCall vyvolá se předání <span> elementu jako parametru (this), který vyvolá metodu UpdateMessage .NET. V UpdateMessage, StateHasChanged je volána k aktualizaci uživatelského rozhraní při message nastavení a display vlastnost druhé <span> se aktualizuje. Pokud StateHasChanged není volána, nemá žádný způsob, jak zjistit, Blazor že uživatelské rozhraní by mělo být aktualizováno při vyvolání metody.

Tato komponenta DotNetObjectReference je uvolněna, když je komponenta uvolněna.

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

Následující nadřazená komponenta obsahuje čtyři položky seznamu, každou instanci ListItem2 komponenty.

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>

Synchronní JS spolupráce v komponentách na straně klienta

Tato část se týká pouze komponent na straně klienta.

JS interop volání jsou asynchronní, bez ohledu na to, zda je volaný kód synchronní nebo asynchronní. Volání jsou asynchronní, aby se zajistilo, že komponenty jsou kompatibilní napříč režimy vykreslování na straně serveru a na straně klienta. Na serveru musí být všechna JS volání zprostředkovatele komunikace asynchronní, protože se odesílají přes síťové připojení.

Pokud víte, že vaše komponenta běží jenom na WebAssembly, můžete se rozhodnout provádět synchronní JS volání interopu. To má o něco menší režii než provádění asynchronních volání a může vést k menšímu počtu cyklů vykreslování, protože při čekání na výsledky neexistuje žádný přechodný stav.

Pokud chcete v komponentě na straně klienta provést synchronní volání z JavaScriptu do .NET, použijte DotNet.invokeMethod místo DotNet.invokeMethodAsync.

Synchronní volání fungují v následujících případech:

  • Komponenta se vykreslí pouze pro provádění ve službě WebAssembly.
  • Volaná funkce vrátí synchronně hodnotu. Funkce není async metoda a nevrací .NET Task ani JavaScript Promise.

Umístění JavaScriptu

Načtěte kód JavaScriptu (JS) pomocí některého z přístupů popsaných v článku o umístění JavaScriptu:

Použití JS modulů k načtení JS je popsáno v tomto článku v izolací JavaScriptu v části moduly JavaScriptu.

Upozorňující

Značku umístěte <script> do souboru komponenty (.razor) pouze v případě, že je zaručeno přijetí statického vykreslování na straně serveru (statické SSR), protože <script> značku nelze dynamicky aktualizovat.

Upozorňující

Neumisťujte <script> značku do souboru komponenty (.razor), protože <script> značku nejde dynamicky aktualizovat.

Izolace JavaScriptu v modulech JavaScriptu

Blazor umožňuje izolaci JavaScriptu (JS) ve standardních modulech JavaScriptu (specifikace ECMAScript). Načítání modulu JavaScriptu funguje stejně jako Blazor u jiných typů webových aplikací a můžete si přizpůsobit, jak se moduly definují ve vaší aplikaci. Průvodce používáním modulů JavaScriptu najdete v tématu MDN Web Docs: Moduly JavaScriptu.

Izolace JS poskytuje následující výhody:

  • Import JS už znečišťuje globální obor názvů.
  • Uživatelé knihovny a komponent už nemusí importovat související JS.

Další informace najdete v tématu Volání funkcí JavaScriptu z metod .NET v ASP.NET Core Blazor.

Dynamický import s operátorem import() se podporuje s ASP.NET Core aBlazor:

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

V předchozím příkladu {CONDITION} zástupný symbol představuje podmíněnou kontrolu, která určuje, jestli se má modul načíst.

Informace o kompatibilitě prohlížeče najdete v tématu Je možné použít: moduly JavaScriptu: dynamický import.

Vyhněte se cyklických odkazům na objekty.

Objekty, které obsahují cyklický odkaz, nelze serializovat v klientovi pro:

  • Volání metody .NET
  • Volání javascriptové metody z jazyka C#, pokud návratový typ obsahuje cyklický odkaz.

Podpora bajtového pole

Blazor podporuje optimalizovanou bajtůovou matici JavaScriptu (JS), která zabraňuje kódování a dekódování bajtových polí do Base64. Následující příklad používá JS zprostředkovatele komunikace k předání bajtového pole do .NET.

sendByteArrayJS Zadejte funkci. Funkce se volá staticky, která zahrnuje parametr názvu sestavení ve invokeMethodAsync volání, tlačítkem v komponentě a nevrací hodnotu:

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>

Poznámka:

Obecné pokyny k JS umístění a doporučení pro produkční aplikace najdete v tématu o umístění JavaScriptu v aplikacích ASP.NET CoreBlazor.

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

Informace o použití bajtového pole při volání JavaScriptu z .NET naleznete v tématu Volání javascriptových funkcí z metod .NET v ASP.NET Core Blazor.

Streamování z JavaScriptu do .NET

Blazor podporuje streamování dat přímo z JavaScriptu do .NET. Datové proudy se požadují pomocí Microsoft.JSInterop.IJSStreamReference rozhraní.

Microsoft.JSInterop.IJSStreamReference.OpenReadStreamAsyncStream vrátí a používá následující parametry:

  • maxAllowedSize: Maximální počet bajtů povolených pro operaci čtení z JavaScriptu, který je ve výchozím nastavení 512 000 bajtů, pokud není zadaný.
  • cancellationToken: A CancellationToken pro zrušení čtení.

V JavaScriptu:

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

V kódu jazyka C#:

var dataReference = 
    await JS.InvokeAsync<IJSStreamReference>("streamToDotNet");
using var dataReferenceStream = 
    await dataReference.OpenReadStreamAsync(maxAllowedSize: 10_000_000);

var outputPath = Path.Combine(Path.GetTempPath(), "file.txt");
using var outputFileStream = File.OpenWrite(outputPath);
await dataReferenceStream.CopyToAsync(outputFileStream);

V předchozím příkladu:

  • JS je vložená IJSRuntime instance. IJSRuntime je registrován v Blazor rámci.
  • Zapíše se dataReferenceStream na disk (file.txt) v cestě k dočasné složce aktuálního uživatele (GetTempPath).

Volání funkcí JavaScriptu z metod .NET v ASP.NET Core Blazor pokrývá zpětnou operaci streamování z .NET do JavaScriptu DotNetStreamReferencepomocí .

ASP.NET nahrání Blazor základního souboru popisuje, jak nahrát soubor do Blazorsouboru . Příklad formulářů, který streamuje <textarea> data v komponentě na straně serveru, najdete v tématu Řešení potíží s formuláři ASP.NET CoreBlazor.

Interoperabilita JavaScriptu [JSImport]/[JSExport]

Tato část se týká komponent na straně klienta.

Jako alternativu k interakci s JavaScriptem (JS) v komponentách na straně klienta pomocí JS Blazormechanismu vzájemné spolupráce založeného na IJSRuntime rozhraní/JS[JSImport][JSExport] je k dispozici rozhraní API vzájemné spolupráce pro aplikace, které cílí na .NET 7 nebo novější.

Další informace naleznete v tématu JavaScript JSImport/JSExport interop s ASP.NET Core Blazor.

Vyřazení odkazů na objekty zprostředkovatele komunikace JavaScriptu

Příklady v článcích interoperability JavaScriptu (JS) ukazují typické vzory odstranění objektů:

JS odkazy na objekty vzájemné spolupráce jsou implementovány jako mapované pomocí identifikátoru na straně JS volání zprostředkovatele komunikace, který vytvoří odkaz. Když je odstranění objektu inicializováno z rozhraní .NET nebo JS ze strany, Blazor odebere položku z mapy a objekt může být uvolněn z paměti, pokud neexistuje žádný jiný silný odkaz na objekt.

Minimálně vždy odstraňte objekty vytvořené na straně .NET, aby nedošlo k úniku spravované paměti .NET.

Úlohy čištění DOM během odstraňování komponent

Další informace najdete v tématu ASP.NET Interoperabilita Core Blazor JavaScriptu (JSinteroperabilita).

Volání zprostředkovatele komunikace JavaScriptu bez okruhu

Další informace najdete v tématu ASP.NET Interoperabilita Core Blazor JavaScriptu (JSinteroperabilita).

Další materiály