Once you have added JavaScript to Blazor, the next step is calling its functions. While calling the JavaScript function, you can also pass data along with the call. In this tutorial, you will discover:
You can download the example code used in this topic on GitHub.
Before going further in this tutorial, you need to learn how to add JavaScript to Blazor first.
A JavaScript function can be a global JavaScript, a function in a module or a function in a class.
A global function is a function that attached to the window
object. You can access the function with the DevTools. The call is initiated by the injected IJSRuntime
object. For example, you have the following JavaScript function:
function exampleFunction() { alert("Hello Blazor School") }
Then you will call the function exampleFunction
as follows:
@inject IJSRuntime JS ... @code { ... public async Task CallFromGlobalAsync() { await JS.InvokeVoidAsync("exampleFunction"); } }
A JavaScript module is defined with the export
keyword. You won't able to access the module by the window
object, even if you have imported it. Because of that, you need to import the JavaScript module and store it as an IJSObjectReference
instance. The call is initiated by the IJSObjectReference
instance. For example, you have the following JavaScript module:
export function exampleFunction() { alert("exampleFunction from isolated.js called"); }
Then you will call the function exampleFunction
as follows:
@inject IJSRuntime JS @implements IAsyncDisposable ... @code { private Lazy<IJSObjectReference> IsolatedModule = new(); ... public async Task CallFromIsolatedAsync() { await IsolatedModule.Value.InvokeVoidAsync("exampleFunction"); } }
Always remember to dispose a module with IAsyncDisposable
.
Sometimes, you want to make use of class declaration of JavaScript. You will need to export a function to create a new instance of that class because Blazor can only call a JavaScript function. For example, you have the following module:
class CollocatedJs { collocateFunction = function () { alert("CollocatedJs module loaded."); } } let instance = new CollocatedJs(); export function CollocatedJsInstance() { return instance; }
Then you need to declare 2 public fields, one for the module and one for the instance, as follows:
@inject IJSRuntime JS @implements IAsyncDisposable ... @code { private Lazy<IJSObjectReference> CollocateJsModule = new(); private Lazy<IJSObjectReference> CollocateJsModuleInstance = new(); }
Load the module along with the instance of the class:
@code { ... protected override async Task OnAfterRenderAsync(bool firstRender) { CollocateJsModule = new(await JS.InvokeAsync<IJSObjectReference>("import", "./Pages/LoadOnDemand/CollocateJS.razor.js")); CollocateJsModuleInstance = new(await CollocateJsModule.Value.InvokeAsync<IJSObjectReference>("CollocatedJsInstance")); } }
Dispose the module and the class after use:
@code { ... public async ValueTask DisposeAsync() { if (CollocateJsModule.IsValueCreated) { await CollocateJsModule.Value.DisposeAsync(); await CollocateJsModuleInstance.Value.DisposeAsync(); } } }
Then you can use the instance of the class to invoke its functions:
await CollocateJsModuleInstance.Value.InvokeVoidAsync("collocateFunction");
Some functions don't return a significant value, but others do. It's important to understand what their values are, how to use them in your code. Both IJSRuntime
and IJSObjectReference
have InvokeAsync
and InvokeVoidAsync
, both methods have the same way of use. The T
in InvokeAsync<T>
is the expected result type of the called function, and the InvokeVoidAsync
is to call a void function.
A predictable function is a function that always return only one specific type of data. For example:
export function predictableResult() { return "Ok"; }
The above function predictableResult
returns a string under any circumstances. Assuming the above module was store in ExampleModule
field. You can get the returned string from the function as follows:
string result = await ExampleModule.Value.InvokeAsync<string>("predictableResult");
An unpredictable function is a function that has a possibility to return different types of data, or even doesn't return anything at all. For example:
export function unpredictableResult() { let randomNum = Math.random() * 10; if (randomNum < 3) { return "Blazor School"; } if (3 < randomNum && randomNum < 6) { return 10; } return; }
To handle such case, you need a try-catch block to catch the JavaScript exception and also expect the result as dynamic
as follows:
try { dynamic unpredictableResult = await ExampleModule.Value.InvokeAsync<dynamic>("unpredictableResult"); } catch { }
You can use both InvokeAsync
and InvokeVoidAsync
to call and also pass parameters to a JavaScript function. In this section, you will learn how to use both methods to pass data from C# code to the JavaScript function.
Assuming you have the following function and is stored to the ExampleModule
field:
export function methodWithPrimitiveParameters(stringData, numberData, dateTimeData) { alert(`Received: string ${stringData}, number ${numberData}, dateTime ${dateTimeData}`); }
Then you can pass the primitive data as follows:
await ExampleModule.Value.InvokeVoidAsync("methodWithPrimitiveParameters", "Blazor School", 5, DateTime.Now);
When you pass a C# object reference, you will allow the JavaScript function to access all of its public methods, but not fields or properties. The following image illustrates the JavaScript accessibility to a C# object.
To pass a C# object reference to a JavaScript function, you need to wrap it first. For example, you have the following JavaScript module, and it is stored in the ExampleModule
field:
export function methodWithReferenceParameter(csharpObjectReference) { alert(`Received object not null? - ${csharpObjectReference != null}`); }
You can pass a C# object reference to this function as follows:
var instanceToPass = new ExampleClass() { ExampleString = "Blazor School", ExampleInt = 100, ExampleDate = DateTime.Now }; var wrappedInstance = DotNetObjectReference.Create<ExampleClass>(instanceToPass); await ExampleModule.Value.InvokeVoidAsync("methodWithReferenceParameter", wrappedInstance);
When you pass a C# object data, you will allow the JavaScript to read all the fields and properties (read only), but JavaScript can't access to the object's methods. The following image illustrates the JavaScript accessibility to a C# object.
For example, you have the following JavaScript module, and it is stored in the ExampleModule
field:
export function methodWithObjectParameter(csharpObject) { alert(`Received object data: string ${csharpObject.exampleString}, number ${csharpObject.exampleInt}, date time ${csharpObject.exampleDate}`); }
You can pass a C# object data to this function as follows:
var instanceToPass = new ExampleClass() { ExampleString = "Blazor School", ExampleInt = 100, ExampleDate = DateTime.Now }; await ExampleModule.Value.InvokeVoidAsync("methodWithObjectParameter", instanceToPass);
The C# object will be serialized to json and pass to the JavaScript function. By default, the JavaScript object will have the camelCase and the C# object will have PascalCase. For example, in C#, the property is namedExampleString
whereas in JavaScript it is namedexampleString
.