Bagikan melalui


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

Catatan

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

Peringatan

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 8 dari artikel ini.

Penting

Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.

Untuk rilis saat ini, lihat versi .NET 9 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.

Memanggil metode .NET statis

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:

  • Tempat {ASSEMBLY NAME} penampung adalah nama rakitan aplikasi.
  • Tempat {.NET METHOD ID} penampung adalah pengidentifikasi metode .NET.
  • Tempat {ARGUMENTS} penampung adalah argumen opsional yang dipisahkan koma untuk diteruskan ke metode , yang masing-masing harus dapat diserialisasikan JSON.
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});

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

Penting

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:

  • Tempat {<T>} penampung menunjukkan jenis pengembalian, yang hanya diperlukan untuk metode yang mengembalikan nilai.
  • Tempat {.NET METHOD ID} penampung adalah pengidentifikasi metode.
@code {
    [JSInvokable]
    public static Task{<T>} {.NET METHOD ID}()
    {
        ...
    }
}

Catatan

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)
        {
            await module.DisposeAsync();
        }
    }
}

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 menambahkan addHandlersJS click peristiwa ke tombol . Fungsi returnArrayAsyncJS ini ditetapkan sebagai handler.

Fungsi ini returnArrayAsyncJS memanggil ReturnArrayAsync metode .NET dari komponen, yang mencatat hasilnya ke konsol alat pengembang web browser. 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)
        {
            await module.DisposeAsync();
        }
    }
}

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 menambahkan addHandlersJS click peristiwa ke tombol . Fungsi returnArrayAsyncJS ini ditetapkan sebagai handler.

Fungsi ini returnArrayAsyncJS memanggil ReturnArrayAsync metode .NET dari komponen, yang mencatat hasilnya ke konsol alat pengembang web browser. 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 <button> HTML elemen onclick adalah penugasan penanganan aktivitas JavaScript onclick untuk memproses click peristiwa, bukan Blazor@onclick atribut direktif. 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>

Catatan

Untuk panduan umum tentang JS lokasi dan rekomendasi kami untuk aplikasi produksi, lihat Lokasi JavaScript di aplikasi ASP.NET CoreBlazor.

Saat tombol Trigger .NET static method dipilih, 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 menunjukkan meneruskan data ke .NET, teruskan posisi awal ke ReturnArrayAsync metode di mana metode dipanggil dalam 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 yang dapat ReturnArrayAsync dipanggil komponen menerima posisi awal dan membangun array darinya. Array dikembalikan untuk pengelogan ke 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)

Catatan

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

ReturnArrayAsync metode yang terdiri dari async kata kunci 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('{ASSEMBLY NAME}', 'ReceiveWindowObject', 
  DotNet.createJSObjectReference(window));
[JSInvokable]
public static void ReceiveWindowObject(IJSObjectReference objRef)
{
    ...
}

Dalam contoh sebelumnya, {ASSEMBLY NAME} tempat penampung adalah namespace aplikasi.

Catatan

Contoh sebelumnya tidak memerlukan pembuangan JSObjectReference, karena referensi ke window objek tidak ditahan di JS.

Mempertahankan referensi ke JSObjectReference perlu membuangnya untuk menghindari kebocoran JS memori pada klien. Contoh berikut merefaktor kode sebelumnya untuk mengambil referensi ke JSObjectReference, diikuti dengan panggilan ke untuk DotNet.disposeJSObjectReference() membuang referensi:

var jsObjectReference = DotNet.createJSObjectReference(window);

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

DotNet.disposeJSObjectReference(jsObjectReference);

Dalam contoh sebelumnya, {ASSEMBLY NAME} tempat penampung adalah namespace aplikasi.

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

Memanggil metode .NET instans

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

  • Teruskan instans .NET dengan merujuk ke JS dengan membungkus instans dalam DotNetObjectReference dan memanggilnya Create .

  • Panggil metode instans .NET dari menggunakan (disarankan) atau invokeMethod (hanya komponen sisi klien) dari yang diteruskan DotNetObjectReference.invokeMethodAsync JS 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.
    • Tempat {.NET METHOD ID} penampung adalah pengidentifikasi metode .NET.
    • Tempat {ARGUMENTS} penampung adalah argumen opsional yang dipisahkan koma untuk diteruskan ke metode , yang masing-masing harus dapat diserialisasikan JSON.
    dotNetHelper.invokeMethodAsync('{.NET METHOD ID}', {ARGUMENTS});
    

    Catatan

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

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

    Penting

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

  • Buang 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 instans kelas, di mana metode .NET yang dapat dipanggil JavaScript yang ditandai dengan [JSInvokable] atribut adalah anggota kelas yang bukan Razor komponen. Ketika metode .NET tersebut terletak di komponen Razor , metode tersebut dilindungi dari runtime relinking/pemangkasan. Untuk melindungi metode .NET dari pemangkasan Razor di luar komponen, terapkan metode dengan DynamicDependency atribut pada konstruktor kelas, seperti yang ditunjukkan 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.

Meneruskan DotNetObjectReference ke fungsi JavaScript individual

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

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

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

Catatan

Untuk panduan umum tentang JS lokasi dan rekomendasi kami untuk aplikasi produksi, lihat Lokasi JavaScript di aplikasi ASP.NET CoreBlazor.

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

Untuk komponen berikut:

  • Komponen memiliki JSmetode .NET yang dapat dipanggil bernama GetHelloMessage.
  • Ketika tombol Trigger .NET instance method dipilih, fungsi sayHello1 dipanggil JS dengan DotNetObjectReference.
  • sayHello1:
    • GetHelloMessage Memanggil dan menerima hasil pesan.
    • 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, nama diteruskan ke metode . 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 kelas dengan beberapa fungsi JavaScript

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

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

Dalam komponen berikut, tombol Trigger JS function memanggil fungsi dengan mengaturonclick JSproperti, bukan Blazor@onclick atribut direktifJS.

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)
        {
            await module.DisposeAsync();
        }

        dotNetHelper?.Dispose();
    }
}

Dalam contoh sebelumnya:

  • JS adalah instans yang disuntikkan IJSRuntime . IJSRuntime terdaftar oleh Blazor kerangka kerja.
  • 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.

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 yang disuntikkan IJSRuntime . IJSRuntime terdaftar oleh Blazor kerangka kerja.
  • 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.

Catatan

Untuk panduan umum tentang JS lokasi dan rekomendasi kami untuk aplikasi produksi, lihat Lokasi JavaScript di aplikasi ASP.NET CoreBlazor.

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 dari Value 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 penulisan ke konsol demi pengelogan aplikasi. Untuk informasi selengkapnya, lihat ASP.NET Pengelogan dan Pengelogan Core Blazor di .NET Core dan ASP.NET Core.

Catatan

Buka jenis dan metode generik tidak menentukan jenis untuk tempat penampung jenis. Sebaliknya, jenis pasokan generik tertutup untuk semua tempat penampung jenis. Contoh di bagian ini menunjukkan generik tertutup, tetapi memanggil metode instans interop dengan generik terbuka didukung.JS 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 void UpdateAsync(TValue newValue)
    {
        await Task.Yield();
        Value = newValue;

        Console.WriteLine($"UpdateAsync: GenericType<{typeof(TValue)}>: {Value}");
    }
}

Dalam fungsi berikut invokeMethodsAsync :

  • Kelas dan UpdateAsync metode jenis Update generik dipanggil dengan argumen yang mewakili 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, DotNetObjectReference panggilan fungsi (invokeMethod atau invokeMethodAsync), metode .NET yang disebut (Update atau UpdateAsync), dan argumen ditulis ke konsol. Argumen menggunakan nomor acak untuk mengizinkan pencocokan JS panggilan fungsi ke pemanggilan metode .NET (juga ditulis ke konsol di sisi .NET). Kode produksi biasanya tidak menulis ke konsol, baik di klien atau server. Aplikasi produksi biasanya mengandalkan pengelogan aplikasi. Untuk informasi selengkapnya, lihat ASP.NET Pengelogan dan Pengelogan Core Blazor di .NET Core 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>

Catatan

Untuk panduan umum tentang JS lokasi dan rekomendasi kami untuk aplikasi produksi, lihat Lokasi JavaScript di aplikasi ASP.NET CoreBlazor.

Dalam komponen GenericsExample berikut:

  • Fungsi JS invokeMethodsAsync ini dipanggil saat tombol Invoke Interop dipilih.
  • Sepasang jenis DotNetObjectReference dibuat dan diteruskan ke JS fungsi untuk instans 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 instans yang disuntikkan IJSRuntime . IJSRuntime terdaftar oleh Blazor kerangka kerja.

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

Output umum 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 instans kelas

Fungsi berikut sayHello1JS :

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

Catatan

Untuk panduan umum tentang JS lokasi dan rekomendasi kami untuk aplikasi produksi, lihat Lokasi JavaScript di aplikasi ASP.NET CoreBlazor.

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 di kelas berikut JsInteropClasses3 memanggil fungsi sayHello1 dengan instans JS baru .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 dirender dengan nama Amy Pond di Name bidang . 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.

Output yang ditampilkan oleh komponen adalah Hello, Amy Pond! ketika nama Amy Pond disediakan di name bidang .

Dalam komponen sebelumnya, referensi objek .NET dibuang. Jika kelas atau komponen tidak membuang DotNetObjectReference, buang dari klien dengan memanggil dispose yang diteruskan DotNetObjectReference:

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

Dalam contoh sebelumnya:

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

Kelas pembantu 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.
  • ListItem1 Ketika tombol komponen dipilih, ListItem1metode itu UpdateMessage mengubah teks item daftar dan menyembunyikan tombol.

Kelas berikut MessageUpdateInvokeHelper mempertahankan JSmetode .NET yang dapat dipanggil, UpdateMessageCaller, untuk memanggil Action yang ditentukan saat kelas dibuat.

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 updateMessageCallerJS memanggil metode .NET.

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

Catatan

Untuk panduan umum tentang JS lokasi dan rekomendasi kami untuk aplikasi produksi, lihat Lokasi JavaScript di aplikasi ASP.NET CoreBlazor.

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 ListItem1 instans komponen menetapkan instans MessageUpdateInvokeHelper dengan set Action ke metodenya UpdateMessage .

ListItem1 Saat tombol komponen InteropCall dipilih, updateMessageCaller dipanggil dengan dibuat DotNetObjectReference untuk MessageUpdateInvokeHelper instans. Ini memungkinkan kerangka kerja untuk memanggil UpdateMessageCaller ListItem1instans tersebut MessageUpdateInvokeHelper . 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 mencakup empat item daftar, setiap instans ListItem1 komponen.

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 dari 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 di mana 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.
  • 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 ListItem2 komponen terdiri dari pesan <span> item daftar dan satu detik <span> dengan properti CSS diatur display ke inline-block untuk ditampilkan.
  • ListItem2 Saat item daftar komponen dipilih, ListItem2metode itu UpdateMessage mengubah teks item daftar di item pertama <span> dan menyembunyikan yang kedua <span> dengan mengatur propertinya display ke 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>

Catatan

Untuk panduan umum tentang JS lokasi dan rekomendasi kami untuk aplikasi produksi, lihat Lokasi JavaScript di aplikasi ASP.NET CoreBlazor.

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 ListItem2 instansJS assignDotNetHelperkomponen memanggil fungsi dengan OnAfterRenderAsync referensi elemen (elemen pertama <span> dari item daftar) dan instans komponen sebagai DotNetObjectReference.

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

dibuang DotNetObjectReference ketika komponen dibuang.

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 void 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)
        {
            await module.DisposeAsync();
        }

        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 void 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)
        {
            await module.DisposeAsync();
        }

        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 mencakup empat item daftar, setiap instans ListItem2 komponen.

CallDotnet7.razor:

@page "/call-dotnet-7"

<PageTitle>Call .NET 7</PageTitle>

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

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotnet7.razor:

@page "/call-dotnet-7"

<PageTitle>Call .NET 7</PageTitle>

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

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotNetExample7.razor:

@page "/call-dotnet-example-7"

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

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotNetExample7.razor:

@page "/call-dotnet-example-7"

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

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotNetExample7.razor:

@page "/call-dotnet-example-7"

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

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotNetExample7.razor:

@page "/call-dotnet-example-7"

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

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

Interop sinkron JS dalam komponen sisi klien

Bagian ini hanya berlaku untuk komponen sisi klien.

JS panggilan interop tidak sinkron, terlepas dari apakah kode yang disebut 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 melakukan 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 eksekusi 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:

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

Peringatan

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.

Peringatan

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 dengan cara Blazor yang sama seperti halnya untuk jenis aplikasi web lainnya, dan Anda bebas menyesuaikan bagaimana modul ditentukan di 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.
  • Konsumen pustaka dan komponen tidak perlu mengimpor JS 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, {CONDITION} tempat penampung mewakili pemeriksaan bersyariah 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 pada klien untuk:

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

Dukungan array byte

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

sendByteArrayJS Menyediakan fungsi. Fungsi ini disebut secara statis, yang mencakup parameter nama rakitan dalam invokeMethodAsync panggilan, dengan tombol 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>

Catatan

Untuk panduan umum tentang JS lokasi dan rekomendasi kami untuk aplikasi produksi, lihat Lokasi JavaScript di aplikasi 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)
        {
            await module.DisposeAsync();
        }
    }
}

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)
        {
            await module.DisposeAsync();
        }
    }
}

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. Aliran diminta menggunakan Microsoft.JSInterop.IJSStreamReference antarmuka .

Microsoft.JSInterop.IJSStreamReference.OpenReadStreamAsyncStream mengembalikan 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: A CancellationToken untuk membatalkan bacaan.

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 yang disuntikkan IJSRuntime . IJSRuntime terdaftar oleh Blazor kerangka kerja.
  • 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 mencakup operasi terbalik, streaming dari .NET ke JavaScript menggunakan DotNetStreamReference.

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

Interop JavaScript [JSImport]/[JSExport]

Bagian ini berlaku untuk komponen sisi klien.

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

Untuk informasi selengkapnya, lihat Interop JavaScript JSImport/JSExport 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: