Memanggil metode .NET dari fungsi JavaScript di ASP.NET Core Blazor

Note

Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 10 dari artikel ini.

Warning

Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 10 dari artikel ini.

Artikel ini menjelaskan cara memanggil metode .NET dari JavaScript (JS).

Untuk informasi tentang cara memanggil JS fungsi dari .NET, lihat Memanggil fungsi JavaScript dari metode .NET di ASP.NET Core Blazor.

Note

Tipe union C# didukung.

Memanggil metode statis .NET

Untuk memanggil metode .NET statis dari JavaScript (JS), gunakan JS fungsi:

  • DotNet.invokeMethodAsync (disarankan): Asinkron untuk komponen sisi server dan sisi klien.
  • DotNet.invokeMethod: Sinkron hanya untuk komponen sisi klien.

Berikan nama rakitan yang berisi metode , pengidentifikasi metode .NET statis, dan argumen apa pun.

Dalam contoh berikut:

  • Placeholder {PACKAGE ID/ASSEMBLY NAME} adalah ID paket proyek (<PackageId> dalam file proyek) untuk pustaka atau nama rakitan aplikasi.
  • {.NET METHOD ID} adalah placeholder untuk pengidentifikasi metode .NET.
  • Placeholder {ARGUMENTS} adalah argumen opsional yang dipisahkan dengan koma untuk diteruskan ke metode, yang masing-masing harus dapat diserialisasi ke format JSON.
DotNet.invokeMethodAsync('{PACKAGE ID/ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});

DotNet.invokeMethodAsync mengembalikan JS Promise yang merepresentasikan hasil operasi. DotNet.invokeMethod (komponen sisi klien) mengembalikan hasil operasi.

Important

Untuk komponen sisi server, kami merekomendasikan fungsi asinkron (invokeMethodAsync) melalui versi sinkron (invokeMethod).

Metode .NET harus publik, statis, dan memiliki [JSInvokable] atribut .

Dalam contoh berikut:

  • Placeholder {<T>} menandakan tipe pengembalian, yang hanya diperlukan untuk metode yang mengembalikan nilai.
  • Placeholder {.NET METHOD ID} merupakan pengidentifikasi metode.
@code {
    [JSInvokable]
    public static Task{<T>} {.NET METHOD ID}()
    {
        ...
    }
}

Note

Memanggil metode generik terbuka tidak didukung dengan metode .NET statis tetapi didukung dengan metode instans. Untuk informasi selengkapnya, lihat bagian Memanggil metode kelas generik .NET.

Dalam komponen berikut, ReturnArrayAsync metode C# mengembalikan int array. Atribut [JSInvokable] diterapkan ke metode , yang membuat metode dapat dipanggil oleh 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);
}

Fungsi addHandlersJS menambahkan kejadian click ke tombol. Fungsi returnArrayAsyncJS ini ditetapkan sebagai handler.

Fungsi returnArrayAsyncJS memanggil metode .NET ReturnArrayAsync dari komponen, yang mencatat hasilnya ke konsol alat pengembang web peramban. BlazorSample adalah nama rakitan aplikasi.

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

Fungsi addHandlersJS menambahkan peristiwa click ke tombol. Fungsi returnArrayAsyncJS ini ditetapkan sebagai handler.

Fungsi returnArrayAsyncJS ini memanggil method .NET dari komponen, yang mencatat hasilnya dalam konsol alat pengembang web peramban. BlazorSample adalah nama rakitan aplikasi.

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 HTML onclick milik elemen <button> adalah penetapan pengendali peristiwa JavaScript onclick untuk memproses peristiwa click, bukan atribut direktif @onclick milik Blazor. Fungsi returnArrayAsyncJS ini ditetapkan sebagai handler.

Fungsi berikut returnArrayAsyncJS , memanggil ReturnArrayAsync metode .NET dari komponen, yang mencatat hasilnya ke konsol alat pengembang web browser. BlazorSample adalah nama rakitan aplikasi.

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

Note

Untuk panduan umum tentang JS lokasi JavaScript dan rekomendasi kami untuk aplikasi produksi, lihat lokasi JavaScript dalam aplikasi ASP.NET Core Blazor.

Saat tombol Trigger .NET static method diklik, output konsol alat pengembang browser menampilkan data array. Format output sedikit berbeda di antara browser. Output berikut menunjukkan format yang digunakan oleh Microsoft Edge:

Array(3) [ 11, 12, 13 ]

Teruskan data ke metode .NET saat memanggil invokeMethodAsync fungsi dengan meneruskan data sebagai argumen.

Untuk mendemonstrasikan cara meneruskan data ke .NET, teruskan posisi awal ke metode ReturnArrayAsync yang dipanggil di 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>

Metode ReturnArrayAsync milik komponen yang dapat dipanggil menerima posisi awal dan menyusun array berdasarkan posisi tersebut. Array tersebut dikembalikan untuk dicatat ke log di konsol:

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

Setelah aplikasi dikompresi ulang dan browser di-refresh, output berikut muncul di konsol browser saat tombol dipilih:

Array(3) [ 14, 15, 16 ]

Pengidentifikasi metode .NET untuk JS panggilan adalah nama metode .NET, tetapi Anda dapat menentukan pengidentifikasi yang berbeda menggunakan [JSInvokable] konstruktor atribut . Dalam contoh berikut, DifferentMethodName adalah pengidentifikasi metode yang ditetapkan untuk metode :ReturnArrayAsync

[JSInvokable("DifferentMethodName")]

Dalam panggilan ke DotNet.invokeMethodAsync (komponen sisi server atau sisi klien) atau DotNet.invokeMethod (hanya komponen sisi klien), panggil DifferentMethodName untuk menjalankan ReturnArrayAsync metode .NET:

  • DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
  • DotNet.invokeMethod('BlazorSample', 'DifferentMethodName'); (hanya komponen sisi klien)

Note

Contoh metode ReturnArrayAsync di bagian ini mengembalikan hasil dari Task tanpa menggunakan kata kunci C# async dan await secara eksplisit. Pola pengodean dengan async dan await merupakan ciri khas metode yang menggunakan kata kunci await untuk mengembalikan nilai dari operasi asinkron.

ReturnArrayAsync metode yang disusun dengan kata kunci async dan await:

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

Untuk informasi selengkapnya, lihat Pemrograman asinkron dengan asinkron dan tunggu di panduan C#.

Buat objek JavaScript dan referensi data untuk diteruskan ke .NET

Panggil DotNet.createJSObjectReference(jsObject) untuk membuat JS referensi objek sehingga dapat diteruskan ke .NET, di mana jsObject digunakan JS Object untuk membuat JS referensi objek. Contoh berikut meneruskan referensi ke objek yang tidak dapat diserialisasikan window ke .NET, yang menerimanya dalam ReceiveWindowObject metode C# sebagai IJSObjectReference:

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

Dalam contoh sebelumnya, {PACKAGE ID/ASSEMBLY NAME} penanda adalah ID paket dari proyek (<PackageId> dalam file proyek) untuk pustaka atau nama assembly untuk aplikasi.

Note

Contoh sebelumnya tidak memerlukan pelepasan JSObjectReference, karena referensi ke objek window tidak disimpan di JS.

Mempertahankan referensi ke JSObjectReference mengharuskan pelepasannya untuk menghindari kebocoran memori JS pada klien. Contoh berikut memfaktorkan ulang kode sebelumnya untuk menyimpan referensi ke JSObjectReference, diikuti dengan pemanggilan DotNet.disposeJSObjectReference() untuk melepaskan referensi tersebut:

var jsObjectReference = DotNet.createJSObjectReference(window);

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

DotNet.disposeJSObjectReference(jsObjectReference);

Dalam contoh sebelumnya, {PACKAGE ID/ASSEMBLY NAME} penanda adalah ID paket dari proyek (<PackageId> dalam file proyek) untuk pustaka atau nama assembly untuk aplikasi.

Panggil DotNet.createJSStreamReference(streamReference) untuk membuat referensi aliran JS sehingga dapat diteruskan ke .NET, di mana streamReference adalah ArrayBuffer, Blob, atau larik bertipe apa pun, seperti Uint8Array atau Float32Array, yang digunakan untuk membuat referensi aliran JS.

Memanggil metode instans .NET

Untuk memanggil metode instans .NET dari JavaScript (JS):

  • Teruskan instans .NET secara referensi ke JS dengan membungkus instans tersebut dalam DotNetObjectReference lalu memanggil Create padanya.

  • Panggil metode instans .NET dari JS menggunakan invokeMethodAsync (disarankan) atau invokeMethod (khusus komponen sisi klien) dari DotNetObjectReference yang diteruskan. Berikan pengidentifikasi metode .NET instans dan argumen apa pun. Instans .NET juga dapat diteruskan sebagai argumen saat memanggil metode .NET lainnya dari JS.

    Dalam contoh berikut:

    • dotNetHelper adalah DotNetObjectReference.
    • {.NET METHOD ID} adalah placeholder untuk pengidentifikasi metode .NET.
    • Placeholder {ARGUMENTS} adalah argumen opsional yang dipisahkan dengan koma untuk diteruskan ke metode, yang masing-masing harus dapat diserialisasi ke format JSON.
    dotNetHelper.invokeMethodAsync('{.NET METHOD ID}', {ARGUMENTS});
    

    Note

    invokeMethodAsync dan invokeMethod tidak menerima parameter nama rakitan saat memanggil metode instans.

    invokeMethodAsync mengembalikan JS Promise yang merepresentasikan hasil operasi. invokeMethod (hanya komponen sisi klien) mengembalikan hasil operasi.

    Important

    Untuk komponen sisi server, kami merekomendasikan fungsi asinkron (invokeMethodAsync) melalui versi sinkron (invokeMethod).

  • Buanglah DotNetObjectReference.

Bagian berikut dari artikel ini menunjukkan berbagai pendekatan untuk memanggil metode .NET instans:

Hindari pemangkasan metode .NET yang dapat dipanggil JavaScript

Bagian ini berlaku untuk aplikasi sisi klien dengan kompilasi ahead-of-time (AOT) dan runtime relinking diaktifkan.

Beberapa contoh di bagian berikut didasarkan pada pendekatan berbasis instance kelas, di mana metode .NET yang dapat dipanggil dari JavaScript yang ditandai dengan atribut [JSInvokable] merupakan anggota dari kelas yang bukan komponen Razor. Ketika metode .NET tersebut terletak di komponen Razor , metode tersebut dilindungi dari runtime relinking/pemangkasan. Untuk melindungi metode .NET dari trimming di luar komponen Razor, implementasikan metode tersebut dengan atribut DynamicDependency pada konstruktor kelas, seperti yang ditunjukkan dalam contoh berikut:

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

public class ExampleClass {

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

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

Untuk informasi selengkapnya, lihat Menyiapkan pustaka .NET untuk pemangkasan: DynamicDependency.

Teruskan DotNetObjectReference ke satu fungsi JavaScript tertentu

Contoh di bagian ini menunjukkan cara meneruskan DotNetObjectReference ke fungsi JavaScript (JS) individual.

Fungsi sayHello1JS berikut menerima DotNetObjectReference dan memanggil invokeMethodAsync untuk memanggil metode .NET GetHelloMessage dari sebuah komponen:

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

Note

Untuk panduan umum tentang lokasi JS dan rekomendasi kami untuk aplikasi produksi, lihat Lokasi JavaScript dalam aplikasi ASP.NET Core Blazor.

Dalam contoh sebelumnya, nama dotNetHelper variabel bersifat arbitrer dan dapat diubah ke nama pilihan apa pun.

Untuk komponen berikut:

  • Komponen ini memiliki metode .NET JS yang dapat dipanggil, bernama GetHelloMessage.
  • Ketika tombol Trigger .NET instance method dipilih, fungsi JS dipanggil sayHello1 dengan DotNetObjectReference.
  • sayHello1:
    • Melakukan panggilan ke GetHelloMessage dan menerima hasil pesan tersebut.
    • Mengembalikan hasil pesan ke metode panggilan TriggerDotNetInstanceMethod .
  • Pesan yang dikembalikan dari sayHello1 masuk result ditampilkan kepada pengguna.
  • Untuk menghindari kebocoran memori dan mengizinkan pengumpulan sampah, referensi objek .NET yang dibuat oleh DotNetObjectReference dibuang dalam Dispose metode .

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

Dalam contoh sebelumnya, nama dotNetHelper variabel bersifat arbitrer dan dapat diubah ke nama pilihan apa pun.

Gunakan panduan berikut untuk meneruskan argumen ke metode instans:

Tambahkan parameter ke pemanggilan metode .NET. Dalam contoh berikut, sebuah nama diberikan ke metode tersebut. Tambahkan parameter tambahan ke daftar sesuai kebutuhan.

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

Dalam contoh sebelumnya, nama dotNetHelper variabel bersifat arbitrer dan dapat diubah ke nama pilihan apa pun.

Berikan daftar parameter ke metode .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();
    }
}

Dalam contoh sebelumnya, nama dotNetHelper variabel bersifat arbitrer dan dapat diubah ke nama pilihan apa pun.

Meneruskan DotNetObjectReference ke dalam kelas dengan beberapa fungsi JavaScript

Contoh di bagian ini menunjukkan cara meneruskan DotNetObjectReference ke kelas JavaScript (JS) dengan beberapa fungsi.

Buat dan teruskan DotNetObjectReference dari metode siklus hidup OnAfterRenderAsync ke class JS agar dapat digunakan oleh beberapa fungsi. Pastikan bahwa kode .NET membuang DotNetObjectReference, seperti yang ditunjukkan contoh berikut.

Dalam komponen berikut, tombol Trigger JS function memanggil fungsi JS dengan menetapkan properti JSonclick, bukan atribut direktif @onclick milik Blazor.

CallDotNetExampleOneHelper.razor:

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

<PageTitle>Call .NET Example</PageTitle>

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

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

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

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

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

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

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

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

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

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

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

        dotNetHelper?.Dispose();
    }
}

Dalam contoh sebelumnya:

  • JS adalah instans IJSRuntime yang diinjeksikan. IJSRuntime didaftarkan oleh kerangka kerja Blazor.
  • Nama dotNetHelper variabel bersifat arbitrer dan dapat diubah ke nama pilihan apa pun.
  • Komponen harus secara eksplisit membuang untuk mengizinkan pengumpulan DotNetObjectReference sampah dan mencegah kebocoran memori.
  • JSDisconnectedException terjebak saat modul dibuang jika sirkuit SignalR milik Blazor terputus. Jika kode sebelumnya digunakan dalam aplikasi Blazor WebAssembly, tidak ada koneksi SignalR yang akan terputus, sehingga Anda dapat menghapus blok try-catch dan membiarkan baris yang melepaskan modul (await module.DisposeAsync();). Untuk informasi selengkapnya, lihat ASP.NET Blazor interoperabilitas Core JavaScript (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);
}

Dalam contoh sebelumnya, nama dotNetHelper variabel bersifat arbitrer dan dapat diubah ke nama pilihan apa pun.

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

Dalam contoh sebelumnya:

  • JS adalah instans IJSRuntime yang diinjeksikan. IJSRuntime didaftarkan oleh kerangka kerja Blazor.
  • Nama dotNetHelper variabel bersifat arbitrer dan dapat diubah ke nama pilihan apa pun.
  • Komponen harus secara eksplisit membuang untuk mengizinkan pengumpulan DotNetObjectReference sampah dan mencegah kebocoran memori.
<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>

Dalam contoh sebelumnya:

  • Kelas GreetingHelpers ditambahkan ke window objek untuk menentukan kelas secara global, yang memungkinkan Blazor untuk menemukan kelas untuk JS interop.
  • Nama dotNetHelper variabel bersifat arbitrer dan dapat diubah ke nama pilihan apa pun.

Note

Untuk panduan umum tentang JS lokasi JavaScript dan rekomendasi kami untuk aplikasi produksi, lihat lokasi JavaScript dalam aplikasi ASP.NET Core Blazor.

Memanggil metode kelas generik .NET

Fungsi JavaScript (JS) dapat memanggil metode kelas generik .NET, di mana JS fungsi memanggil metode .NET dari kelas generik.

Di kelas jenis generik berikut (GenericType<TValue>):

  • Kelas memiliki parameter jenis tunggal (TValue) dengan satu properti generik Value .
  • Kelas ini memiliki dua metode non-generik yang ditandai dengan [JSInvokable] atribut , masing-masing dengan parameter jenis generik bernama newValue:
    • Update secara sinkron memperbarui nilai Value dari newValue.
    • UpdateAsync secara asinkron memperbarui nilai Value dari newValue setelah membuat tugas yang dapat ditunggu dengan Task.Yield yang secara asinkron menghasilkan kembali ke konteks saat ini ketika ditunggu.
  • Masing-masing metode kelas menulis jenis TValue dan nilai Value ke konsol. Menulis ke konsol hanya untuk tujuan demonstrasi. Aplikasi produksi biasanya menghindari menulis ke konsol dan lebih memilih pencatatan log aplikasi. Untuk informasi selengkapnya, lihat Blazor dan Pencatatan di .NET dan ASP.NET Core.

Note

Tipe dan metode generik terbuka tidak menentukan tipe untuk placeholder tipe. Sebaliknya, generik tertutup menyediakan tipe untuk semua placeholder tipe. Contoh di bagian ini menunjukkan generik tertutup, tetapi memanggil JS interop metode instans dengan generik terbuka didukung. Penggunaan generik terbuka tidak didukung untuk pemanggilan metode .NET statis, yang dijelaskan sebelumnya dalam artikel ini.

Untuk informasi lebih lanjut, baca artikel berikut:

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

Dalam fungsi berikut invokeMethodsAsync :

  • Metode Update dan UpdateAsync milik kelas tipe generik dipanggil dengan argumen yang merepresentasikan string dan angka.
  • Komponen sisi klien mendukung panggilan metode .NET secara sinkron dengan invokeMethod. syncInterop menerima nilai boolean yang menunjukkan apakah JS interop terjadi pada klien. Ketika syncInterop adalah true, invokeMethod dipanggil dengan aman. Jika nilai syncInterop adalah false, hanya fungsi invokeMethodAsync asinkron yang dipanggil karena JS interop dijalankan dalam komponen sisi server.
  • Untuk tujuan demonstrasi, pemanggilan fungsi DotNetObjectReference (invokeMethod atau invokeMethodAsync), metode .NET yang dipanggil (Update atau UpdateAsync), dan argumennya ditampilkan di konsol. Argumen menggunakan angka acak untuk memungkinkan pencocokan panggilan fungsi JS dengan pemanggilan metode .NET (juga ditampilkan di konsol pada sisi .NET). Kode produksi biasanya tidak menulis ke konsol, baik di klien atau server. Aplikasi produksi biasanya mengandalkan pencatatan log aplikasi. Untuk informasi selengkapnya, lihat Blazor dan Pencatatan di .NET dan ASP.NET Core.
<script>
  const randomInt = () => Math.floor(Math.random() * 99999);

  window.invokeMethodsAsync = async (syncInterop, dotNetHelper1, dotNetHelper2) => {
    var n = randomInt();
    console.log(`JS: invokeMethodAsync:Update('string ${n}')`);
    await dotNetHelper1.invokeMethodAsync('Update', `string ${n}`);

    n = randomInt();
    console.log(`JS: invokeMethodAsync:UpdateAsync('string ${n}')`);
    await dotNetHelper1.invokeMethodAsync('UpdateAsync', `string ${n}`);

    if (syncInterop) {
      n = randomInt();
      console.log(`JS: invokeMethod:Update('string ${n}')`);
      dotNetHelper1.invokeMethod('Update', `string ${n}`);
    }

    n = randomInt();
    console.log(`JS: invokeMethodAsync:Update(${n})`);
    await dotNetHelper2.invokeMethodAsync('Update', n);

    n = randomInt();
    console.log(`JS: invokeMethodAsync:UpdateAsync(${n})`);
    await dotNetHelper2.invokeMethodAsync('UpdateAsync', n);

    if (syncInterop) {
      n = randomInt();
      console.log(`JS: invokeMethod:Update(${n})`);
      dotNetHelper2.invokeMethod('Update', n);
    }
  };
</script>

Note

Untuk panduan umum tentang JS lokasi JavaScript dan rekomendasi kami untuk aplikasi produksi, lihat lokasi JavaScript dalam aplikasi ASP.NET Core Blazor.

Dalam komponen GenericsExample berikut:

  • Fungsi JSinvokeMethodsAsync ini dipanggil saat tombol Invoke Interop dipilih.
  • Dua jenis DotNetObjectReference dibuat dan diteruskan ke fungsi JS untuk instance GenericType sebagai string dan 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();
    }
}

Dalam contoh sebelumnya, JS adalah instance IJSRuntime yang disuntikkan. IJSRuntime didaftarkan oleh kerangka kerja Blazor.

Berikut ini menunjukkan output umum dari contoh sebelumnya saat tombol Invoke Interop dipilih dalam komponen sisi klien:

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

Jika contoh sebelumnya diimplementasikan dalam komponen sisi server, panggilan sinkron dengan invokeMethod dihindari. Untuk komponen sisi server, kami merekomendasikan fungsi asinkron (invokeMethodAsync) melalui versi sinkron (invokeMethod).

Keluaran khas komponen sisi server:

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

Contoh output sebelumnya menunjukkan bahwa metode asinkron dijalankan dan diselesaikan dalam urutan arbitrer tergantung pada beberapa faktor, termasuk penjadwalan utas dan kecepatan eksekusi metode. Tidak dimungkinkan untuk memprediksi urutan penyelesaian panggilan metode asinkron dengan andal.

Contoh instance kelas

Fungsi berikut sayHello1JS :

  • GetHelloMessage Memanggil metode .NET pada DotNetObjectReference yang diteruskan.
  • Mengembalikan pesan dari GetHelloMessage ke pemanggil sayHello1 .
<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

Note

Untuk panduan umum tentang JS lokasi JavaScript dan rekomendasi kami untuk aplikasi produksi, lihat lokasi JavaScript dalam aplikasi ASP.NET Core Blazor.

Dalam contoh sebelumnya, nama dotNetHelper variabel bersifat arbitrer dan dapat diubah ke nama pilihan apa pun.

Kelas berikut HelloHelper memiliki JSmetode .NET yang dapat dipanggil bernama GetHelloMessage. Saat HelloHelper dibuat, nama dalam Name properti digunakan untuk mengembalikan pesan dari 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}!";
}

Metode CallHelloHelperGetHelloMessage dalam kelas JsInteropClasses3 berikut memanggil fungsi JSsayHello1 dengan instans baru dari 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);
    }
}

Untuk menghindari kebocoran memori dan mengizinkan pengumpulan sampah, referensi objek .NET yang dibuat oleh DotNetObjectReference dibuang ketika referensi objek keluar dari cakupan dengan using var sintaksis.

Ketika tombol Trigger .NET instance method dipilih dalam komponen berikut, JsInteropClasses3.CallHelloHelperGetHelloMessage dipanggil dengan nilai 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);
    }
}

Gambar berikut menunjukkan komponen yang ditampilkan dengan nama Amy Pond pada kolom Name. Setelah tombol dipilih, Hello, Amy Pond! ditampilkan di UI:

Contoh komponen 'CallDotNetExample4' yang dirender

Pola sebelumnya yang ditunjukkan di JsInteropClasses3 kelas juga dapat diimplementasikan sepenuhnya dalam komponen.

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

Untuk menghindari kebocoran memori dan mengizinkan pengumpulan sampah, referensi objek .NET yang dibuat oleh DotNetObjectReference dibuang ketika referensi objek keluar dari cakupan dengan using var sintaksis.

Keluaran yang ditampilkan oleh komponen adalah Hello, Amy Pond! ketika nama Amy Pond dimasukkan dalam bidang name.

Dalam komponen sebelumnya, referensi objek .NET dibuang. Jika suatu kelas atau komponen tidak melepaskan DotNetObjectReference, lepaskan dari klien dengan memanggil dispose pada DotNetObjectReference yang diteruskan:

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

Dalam contoh sebelumnya:

  • Placeholder {JS FUNCTION NAME} adalah nama fungsi JS.
  • Nama dotNetHelper variabel bersifat arbitrer dan dapat diubah ke nama pilihan apa pun.
  • {.NET METHOD ID} adalah placeholder untuk pengidentifikasi metode .NET.

Kelas pembantu untuk metode .NET instans komponen

Kelas pembantu dapat memanggil metode instans .NET sebagai Action. Kelas pembantu berguna dalam skenario saat menggunakan metode .NET statis tidak berlaku:

  • Ketika beberapa komponen dengan jenis yang sama dirender pada halaman yang sama.
  • Di aplikasi sisi server dengan beberapa pengguna secara bersamaan menggunakan komponen yang sama.

Dalam contoh berikut:

  • Komponen berisi beberapa ListItem1 komponen.
  • Setiap ListItem1 komponen terdiri dari pesan dan tombol.
  • Ketika tombol komponen ListItem1 dipilih, metode UpdateMessage milik ListItem1 tersebut mengubah teks butir daftar dan menyembunyikan tombol.

Kelas MessageUpdateInvokeHelper berikut menyediakan metode .NET JS yang dapat dipanggil, UpdateMessageCaller, untuk memanggil Action yang ditentukan saat kelas diinisialisasi.

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

Fungsi updateMessageCaller berikut JSUpdateMessageCaller memanggil metode .NET.

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

Note

Untuk panduan umum tentang JS lokasi JavaScript dan rekomendasi kami untuk aplikasi produksi, lihat lokasi JavaScript dalam aplikasi ASP.NET Core Blazor.

Dalam contoh sebelumnya, nama dotNetHelper variabel bersifat arbitrer dan dapat diubah ke nama pilihan apa pun.

Komponen berikut ListItem1 adalah komponen bersama yang dapat digunakan berapa kali dalam komponen induk dan membuat item daftar (<li>...</li>) untuk daftar HTML (<ul>...</ul> atau <ol>...</ol>). Setiap instance ListItem1 membuat sebuah instance MessageUpdateInvokeHelper dengan Action disetel ke metode UpdateMessage-nya.

Saat tombol InteropCall milik komponen ListItem1 dipilih, updateMessageCaller dipanggil dengan DotNetObjectReference yang dibuat untuk instance MessageUpdateInvokeHelper. Ini memungkinkan kerangka kerja untuk memanggil UpdateMessageCaller pada instans MessageUpdateInvokeHelper milik ListItem1 tersebut. Yang diteruskan DotNetObjectReference dibuang di 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 dipanggil untuk memperbarui UI saat message diatur dalam UpdateMessage. Jika StateHasChanged tidak dipanggil, Blazor tidak memiliki cara untuk mengetahui bahwa UI harus diperbarui ketika Action dipanggil.

Komponen induk berikut berisi empat item daftar, yang masing-masing merupakan instans dari komponen 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>

Gambar berikut menunjukkan komponen induk yang dirender setelah tombol kedua InteropCall dipilih:

  • Komponen kedua ListItem1 telah menampilkan UpdateMessage Called! pesan.
  • Tombol InteropCall untuk komponen kedua ListItem1 tidak terlihat karena properti CSS display tombol diatur ke none.

Contoh komponen 'CallDotNetExample6' yang dirender

Metode .NET instans komponen yang dipanggil melalui DotNetObjectReference ditetapkan ke properti elemen

Penugasan DotNetObjectReference ke properti elemen HTML memungkinkan panggilan metode .NET pada instans komponen:

Mirip dengan pendekatan yang dijelaskan di bagian kelas pembantu metode .NET instans komponen, pendekatan ini berguna dalam skenario saat metode .NET statis tidak dapat digunakan:

  • Ketika beberapa komponen dengan jenis yang sama dirender pada halaman yang sama.
  • Di aplikasi sisi server dengan beberapa pengguna secara bersamaan menggunakan komponen yang sama.
  • Metode .NET dipanggil dari JS peristiwa (misalnya, onclick), bukan dari Blazor peristiwa (misalnya, @onclick).

Dalam contoh berikut:

  • Komponen berisi beberapa ListItem2 komponen, yang merupakan komponen bersama.
  • Setiap komponen ListItem2 terdiri atas pesan item daftar <span> dan elemen kedua <span> dengan properti CSS display yang diatur ke inline-block untuk tampilan.
  • Saat item daftar komponen ListItem2 dipilih, metode UpdateMessage milik ListItem2 tersebut mengubah teks item daftar dalam <span> pertama dan menyembunyikan <span> kedua dengan menyetel properti display-nya menjadi none.

Fungsi berikut assignDotNetHelperJS menetapkan DotNetObjectReference ke elemen dalam properti bernama dotNetHelper. Fungsi berikut interopCallJS menggunakan DotNetObjectReference untuk elemen yang diteruskan untuk memanggil metode .NET bernama UpdateMessage.

ListItem2.razor.js:

export function assignDotNetHelper(element, dotNetHelper) {
  element.dotNetHelper = dotNetHelper;
}

export async function interopCall(element) {
  await element.dotNetHelper.invokeMethodAsync('UpdateMessage');
}

ListItem2.razor.js:

export function assignDotNetHelper(element, dotNetHelper) {
  element.dotNetHelper = dotNetHelper;
}

export async function interopCall(element) {
  await element.dotNetHelper.invokeMethodAsync('UpdateMessage');
}
<script>
  window.assignDotNetHelper = (element, dotNetHelper) => {
    element.dotNetHelper = dotNetHelper;
  }

  window.interopCall = async (element) => {
    await element.dotNetHelper.invokeMethodAsync('UpdateMessage');
  }
</script>

Note

Untuk panduan umum tentang JS lokasi JavaScript dan rekomendasi kami untuk aplikasi produksi, lihat lokasi JavaScript dalam aplikasi ASP.NET Core Blazor.

Dalam contoh sebelumnya, nama dotNetHelper variabel bersifat arbitrer dan dapat diubah ke nama pilihan apa pun.

Komponen berikut ListItem2 adalah komponen bersama yang dapat digunakan berapa kali dalam komponen induk dan membuat item daftar (<li>...</li>) untuk daftar HTML (<ul>...</ul> atau <ol>...</ol>).

Setiap instans komponen ListItem2 memanggil fungsi assignDotNetHelperJS di OnAfterRenderAsync dengan referensi elemen (elemen <span> pertama dari item daftar) dan instans komponen sebagai DotNetObjectReference.

Ketika pesan komponen ListItem2<span> dipilih, interopCall dipanggil dengan meneruskan elemen <span> sebagai parameter (this), yang kemudian memanggil metode .NET UpdateMessage. Dalam UpdateMessage, StateHasChanged dipanggil untuk memperbarui UI saat message diatur dan properti display dari <span> kedua diperbarui. Jika StateHasChanged tidak dipanggil, Blazor tidak memiliki cara untuk mengetahui bahwa UI harus diperbarui ketika metode dipanggil.

DotNetObjectReference akan dilepas saat komponen dilepas.

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

Komponen induk berikut berisi empat item daftar, yang masing-masing merupakan instans dari komponen 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>

Interoperabilitas sinkron JS pada komponen sisi klien

Bagian ini hanya berlaku untuk komponen sisi klien.

JS panggilan interop bersifat asinkron, terlepas dari apakah kode yang dipanggil bersifat sinkron atau asinkron. Panggilan bersifat asinkron untuk memastikan bahwa komponen kompatibel di seluruh mode render sisi server dan sisi klien. Di server, semua JS panggilan interop harus asinkron karena dikirim melalui koneksi jaringan.

Jika Anda tahu dengan pasti bahwa komponen Anda hanya berjalan di WebAssembly, Anda dapat memilih untuk membuat panggilan interop sinkron JS. Ini memiliki overhead yang sedikit lebih sedikit daripada melakukan panggilan asinkron dan dapat mengakibatkan lebih sedikit siklus render karena tidak ada status menengah saat menunggu hasil.

Untuk melakukan panggilan sinkron dari JavaScript ke .NET dalam komponen sisi klien, gunakan DotNet.invokeMethod alih-alih DotNet.invokeMethodAsync.

Panggilan sinkron berfungsi jika:

  • Komponen hanya dirender untuk dijalankan di WebAssembly.
  • Fungsi yang disebut mengembalikan nilai secara sinkron. Fungsi ini bukan async metode dan tidak mengembalikan .NET Task atau JavaScript Promise.

Lokasi JavaScript

Muat kode JavaScript (JS) menggunakan salah satu pendekatan yang dijelaskan oleh artikel tentang lokasi JavaScript:

Penggunaan modul JS untuk memuat JS dijelaskan dalam artikel ini di bagian isolasi JavaScript dalam modul JavaScript.

Warning

Hanya tempatkan <script> tag dalam file komponen (.razor) jika komponen dijamin untuk mengadopsi penyajian sisi server statis (SSR statis) karena <script> tag tidak dapat diperbarui secara dinamis.

Warning

Jangan menempatkan <script> tag dalam file komponen (.razor) karena <script> tag tidak dapat diperbarui secara dinamis.

Isolasi JavaScript dalam modul JavaScript

Blazor mengaktifkan isolasi JavaScript (JS) dalam modul JavaScript standar (spesifikasi ECMAScript). Pemuatan modul JavaScript berfungsi sama seperti pada Blazor dan jenis aplikasi web lainnya, dan Anda bebas menyesuaikan cara modul ditentukan dalam aplikasi Anda. Untuk panduan tentang cara menggunakan modul JavaScript, lihat MDN Web Docs: Modul JavaScript.

JS isolasi memberikan manfaat berikut:

  • JS yang diimpor tidak lagi mencemari namespace global.
  • Pengguna pustaka dan komponen tidak diharuskan untuk mengimpor JS yang terkait.

Untuk informasi lebih lanjut, lihat Memanggil fungsi JavaScript dari metode .NET di Blazor ASP.NET Core.

Impor dinamis dengan import() operator didukung dengan ASP.NET Core dan Blazor:

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

Dalam contoh sebelumnya, placeholder {CONDITION} mewakili pengecekan kondisi untuk menentukan apakah modul harus dimuat.

Untuk kompatibilitas browser, lihat Dapatkah saya menggunakan: modul JavaScript: impor dinamis.

Hindari referensi objek melingkar

Objek yang berisi referensi melingkar tidak dapat diserialisasikan di sisi klien untuk keduanya:

  • Panggilan metode .NET.
  • Metode JavaScript memanggil dari C# ketika jenis pengembalian memiliki referensi melingkar.

Dukungan array byte

Blazor mendukung interoperabilitas JavaScript (JS) array byte yang dioptimalkan, yang menghindari pengodean/pendekodean array byte menjadi Base64. Contoh berikut menggunakan JS interop untuk meneruskan array byte ke .NET.

Menyediakan sendByteArrayJS sebuah fungsi. Fungsi ini dipanggil secara statis, yang mencakup parameter nama assembly dalam pemanggilan invokeMethodAsync, oleh tombol di dalam komponen dan tidak mengembalikan nilai:

CallDotnet8.razor.js:

export function sendByteArray() {
  const data = new Uint8Array([0x45, 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69,
    0x6e, 0x67, 0x27, 0x73, 0x20, 0x73, 0x68, 0x69, 0x6e, 0x79, 0x2c,
    0x20, 0x43, 0x61, 0x70, 0x74, 0x61, 0x69, 0x6e, 0x2e, 0x20, 0x4e,
    0x6f, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x72, 0x65, 0x74, 0x2e]);
  DotNet.invokeMethodAsync('BlazorSample', 'ReceiveByteArray', data)
    .then(str => {
      alert(str);
    });
}

export function addHandlers() {
  const btn = document.getElementById("btn");
  btn.addEventListener("click", sendByteArray);
}
<script>
  window.sendByteArray = () => {
    const data = new Uint8Array([0x45,0x76,0x65,0x72,0x79,0x74,0x68,0x69,
      0x6e,0x67,0x27,0x73,0x20,0x73,0x68,0x69,0x6e,0x79,0x2c,
      0x20,0x43,0x61,0x70,0x74,0x61,0x69,0x6e,0x2e,0x20,0x4e,
      0x6f,0x74,0x20,0x74,0x6f,0x20,0x66,0x72,0x65,0x74,0x2e]);
    DotNet.invokeMethodAsync('BlazorSample', 'ReceiveByteArray', data)
      .then(str => {
        alert(str);
      });
  };
</script>

Note

Untuk panduan umum tentang JS lokasi JavaScript dan rekomendasi kami untuk aplikasi produksi, lihat lokasi JavaScript dalam aplikasi ASP.NET Core Blazor.

CallDotnet8.razor:

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

<PageTitle>Call .NET 8</PageTitle>

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

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

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

@code {
    private IJSObjectReference? module;

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

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

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

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

CallDotnet8.razor:

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

<PageTitle>Call .NET 8</PageTitle>

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

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

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

@code {
    private IJSObjectReference? module;

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

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

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

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

CallDotNetExample8.razor:

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

<PageTitle>Call .NET 8</PageTitle>

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

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

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

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

CallDotNetExample8.razor:

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

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

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

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

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

Untuk informasi tentang menggunakan array byte saat memanggil JavaScript dari .NET, lihat Memanggil fungsi JavaScript dari metode .NET di ASP.NET Core Blazor.

Streaming dari JavaScript ke .NET

Blazor mendukung streaming data langsung dari JavaScript ke .NET. Stream diminta melalui antarmuka Microsoft.JSInterop.IJSStreamReference.

Microsoft.JSInterop.IJSStreamReference.OpenReadStreamAsync mengembalikan sebuah Stream dan menggunakan parameter berikut:

  • maxAllowedSize: Jumlah maksimum byte yang diizinkan untuk operasi baca dari JavaScript, yang defaultnya adalah 512.000 byte jika tidak ditentukan.
  • cancellationToken: Sebuah CancellationToken untuk membatalkan operasi baca.

Di JavaScript:

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

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

Dalam contoh sebelumnya:

  • JS adalah instans IJSRuntime yang diinjeksikan. IJSRuntime didaftarkan oleh kerangka kerja Blazor.
  • dataReferenceStream ditulis ke disk (file.txt) di jalur folder sementara pengguna saat ini (GetTempPath).

Memanggil fungsi JavaScript dari metode .NET di ASP.NET Core Blazor membahas kebalikannya, yaitu streaming dari .NET ke JavaScript menggunakan DotNetStreamReference.

Pengunggahan file ASP.NET Core Blazor mencakup cara mengunggah file di Blazor. Untuk contoh formulir yang melakukan streaming <textarea> data dalam komponen sisi server, lihat Memecahkan masalah formulir ASP.NET Core Blazor.

Interop JavaScript [JSImport]/[JSExport]

Bagian ini berlaku untuk komponen sisi klien.

Sebagai alternatif untuk berinteraksi dengan JavaScript (JS) dalam komponen sisi klien dengan menggunakan mekanisme interop BlazorJS berdasarkan antarmuka IJSRuntime, tersedia API interop JS[JSImport]/[JSExport] untuk aplikasi yang menargetkan .NET 7 atau yang lebih baru.

Untuk informasi selengkapnya, lihat Interoperabilitas JSImport/JSExport JavaScript dengan ASP.NET Core Blazor.

Pembuangan referensi objek interop JavaScript

Contoh di seluruh artikel interop JavaScript (JS) menunjukkan pola pembuangan objek umum:

JS referensi objek interop diimplementasikan sebagai peta yang dikunci oleh pengidentifikasi di sisi JS panggilan interop yang membuat referensi. Ketika pembuangan objek dimulai dari .NET atau JS sisi, Blazor menghapus entri dari peta, dan objek dapat menjadi sampah yang dikumpulkan selama tidak ada referensi kuat lainnya ke objek yang ada.

Minimal, selalu buang objek yang dibuat di sisi .NET untuk menghindari kebocoran memori terkelola .NET.

Tugas pembersihan DOM selama pembuangan komponen

Untuk informasi selengkapnya, lihat ASP.NET Blazor interoperabilitas Core JavaScript (JS interop).

Panggilan interop JavaScript tanpa sirkuit

Untuk informasi selengkapnya, lihat ASP.NET Blazor interoperabilitas Core JavaScript (JS interop).

Sumber daya tambahan