IJSRuntime
Kết nối C# với JavaScript: gọi hàm, nhận kết quả và quản lý JavaScript module trong component.
Thử hai cách gọi JavaScript bên dưới: hàm toàn cục và ES module. Kết quả xuất hiện ngay trong mỗi khối; mở Xem code để đối chiếu lời gọi C# với hàm JavaScript tương ứng.
01. Global JS — gọi hàm toàn cục
Cách này dùng khi hàm JavaScript được khai báo toàn cục
(ví dụ trong window hoặc file JS load trực tiếp vào page).
Xem code
<button class="btn btn-primary" @onclick="Global_ShowAlert">Alert</button>
<button class="btn btn-success" @onclick="Global_GetBrowserInfo">Get Browser Info</button>
<button class="btn btn-warning" @onclick="Global_GetCurrentTime">Get Current Time</button>
<p><b>Global Result:</b> @GlobalResult</p>
@code {
private string GlobalResult = "none";
private async Task Global_ShowAlert()
{
await JS.InvokeVoidAsync("blazorGlobal.showAlert", "Hello from Blazor (Global JS)");
GlobalResult = "✅ Alert đã được gọi";
}
private async Task Global_GetBrowserInfo()
{
GlobalResult = await JS.InvokeAsync<string>("blazorGlobal.getBrowserInfo");
}
private async Task Global_GetCurrentTime()
{
GlobalResult = await JS.InvokeAsync<string>("blazorGlobal.getCurrentTime");
}
}
window.blazorGlobal = {
showAlert: function (message) {
alert(message);
},
getBrowserInfo: function () {
return `UserAgent: ${navigator.userAgent}`;
},
getCurrentTime: function () {
return new Date().toLocaleTimeString();
}
};
02. JS Module — import rồi gọi hàm
Cách này dùng với file ES module. Đây là cách hiện đại hơn, gọn scope và dễ quản lý hơn so với global function.
Xem code
@inject IJSRuntime JS
@implements IAsyncDisposable
<button class="btn btn-dark" @onclick="Module_Init">Init Module</button>
<button class="btn btn-info text-white" @onclick="Module_AddNumbers">Add 10 + 20</button>
<button class="btn btn-secondary" @onclick="Module_FormatMessage">Format Message</button>
<button class="btn btn-outline-danger" @onclick="Module_Dispose">Dispose Module</button>
<p><b>Module Ready:</b> @ModuleReady</p>
<p><b>Module Result:</b> @ModuleResult</p>
@code {
private IJSObjectReference? _module;
private string ModuleResult = "none";
private bool ModuleReady = false;
private async Task EnsureModuleAsync()
{
if (_module is null)
{
_module = await JS.InvokeAsync<IJSObjectReference>(
"import",
"./js/ijsruntime-module-demo.js");
ModuleReady = true;
}
}
private async Task Module_Init()
{
await EnsureModuleAsync();
ModuleResult = "Module initialized";
}
private async Task Module_AddNumbers()
{
await EnsureModuleAsync();
var result = await _module!.InvokeAsync<int>("addNumbers", 10, 20);
ModuleResult = $"10 + 20 = {result}";
}
private async Task Module_FormatMessage()
{
await EnsureModuleAsync();
ModuleResult = await _module!.InvokeAsync<string>(
"formatMessage",
"Hello from Blazor Module");
}
private async Task Module_Dispose()
{
if (_module is not null)
{
await _module.DisposeAsync();
_module = null;
ModuleReady = false;
ModuleResult = "Module disposed";
}
}
public async ValueTask DisposeAsync()
{
if (_module is not null)
{
await _module.DisposeAsync();
}
}
}
export function addNumbers(a, b) {
return a + b;
}
export function formatMessage(message) {
return `[Module] ${message} at ${new Date().toLocaleTimeString()}`;
}
03. Chọn Global hay Module?
- Global JS: gọi nhanh, phù hợp demo nhỏ hoặc code cũ, nhưng dễ làm ô nhiễm global scope.
- JS Module: hiện đại hơn, tách scope rõ ràng, dễ bảo trì và nên ưu tiên trong dự án lớn.
-
Khi dùng module, nhớ dispose
IJSObjectReferencenếu component có thể bị hủy nhiều lần.
04. JavaScript gọi ngược về C#
Result: none
JavaScript có thể gọi trực tiếp method C# bằng [JSInvokable].
Xem code
<button @onclick="CallJsToInvokeDotNet">
JS gọi C#
</button>
<p><b>Result:</b> @DotNetResult</p>
private string DotNetResult = "none";
private async Task CallJsToInvokeDotNet()
{
await JS.InvokeVoidAsync("blazorGlobal.callDotNet");
}
[JSInvokable]
public static string DotNetHello()
{
return "Hello from C#";
}
window.blazorGlobal.callDotNet = async function () {
const result = await DotNet.invokeMethodAsync(
'BlazorSample',
'DotNetHello'
);
alert(result);
}
Dùng InvokeVoidAsync khi không cần nhận kết quả, InvokeAsync<T> khi có dữ liệu trả về.
Với module, giữ tham chiếu trong lúc dùng và giải phóng khi component kết thúc.