This content originally appeared on Telerik Blogs and was authored by Claudio Bernasconi
We learn how to call .NET methods from JavaScript functions in Blazor Server and Blazor WebAssembly applications.
JavaScript is very popular for web development. With Blazor, we have an alternative that allows us to use C# instead of JavaScript for building modern, interactive, single-page web applications.
But sometimes, we want to take advantage of existing JavaScript code.
In a previous article of this series, we learned how to integrate JavaScript code into a Blazor web application by calling JavaScript code from our C# code. In this article, we want to learn about the other way around. We will learn how to call .NET code from JavaScript.
You can access the code used in this example on GitHub.
Calling a Simple C# Method from JavaScript
We want to build a simple chat application in which chat messages are created in JavaScript code and sent to a Blazor component.
We start by implementing the Blazor component. For this example, I created a Blazor Server web application using the Blazor Web App project template coming with .NET 8.
Hint: Don’t shy away if you prefer Blazor WebAssembly interactivity. The interoperability code I share in this article also works for Blazor WebAssembly.
We implement the chat application inside the Home
page component.
@page "/"
@rendermode InteractiveServer
@inject IJSRuntime JS
@implements IDisposable
<PageTitle>Home</PageTitle>
<h1>Chat from JavaScript!</h1>
<h2>Messages</h2>
@foreach (var message in ChatMessages)
{
<p>@message</p>
}
We use the @page
directive as generated by the project template to make the Home
component a routable page component.
The @rendermode
directive is required when using a per page/component interactivity location when generating the project. If you have a global Blazor Server interactivity application, you don’t need this line.
The @inject IJSRuntime
directive provides access to the JavaScript runtime.
The @implement IDisposable
directive makes this component implement the Dispose
method. We will learn about why we need to implement the IDisposable
interface when implementing the behavior of the Home
component.
The component code uses the built-in PageTitle
component to render the title visible in the browser window or tab.
Next, we use static HTML elements to display headers.
The most interesting part of the template code is the foreach
where we render the chat messages stored in the ChatMessages
property using an HTML paragraph element.
Now, let’s implement the interactive code in the @code
section.
@code {
public IList<string> ChatMessages { get; set; } = new List<string>();
public DotNetObjectReference<Home>? DotNetReference { get; set; }
}
We have two properties. The first property, ChatMessages
of type IList<string>
, holds the chat messages we want to display on the page.
The second property, called DotNetReference
of the generic DotNetObjectReference
type, holds a reference to a .NET type. In our scenario, use Home
as the generic type argument since we want to pass a reference to the Home
component to the JavaScript code.
Next, we implement three methods. We start with the OnAfterRender
lifecycle method.
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
if (firstRender)
{
DotNetReference = DotNetObjectReference.Create(this);
JS.InvokeVoidAsync("generateChatMessages", DotNetReference);
}
}
In the OnAfterRender
lifecycle method, we call the method of the base class and check whether this execution is the first rendering of the component.
In the case of the first rendering, we create a reference to the .NET object using the static DotNetObjectReference.Create
method. We provide this as its argument because we want to create a reference to the Home
component that we are currently implementing.
Next, we use the JS property and its InvokeVoidAsync
method to call a JavaScript function from the .NET code. In this example, it’s our way of calling the JavaScript code, which we later use to call the .NET code.
We will implement the generateChatMessages
function below.
First, we want to implement the method that we will call from the JavaScript code.
[JSInvokable("AddMessage")]
public Task AddChatMessage(string message)
{
ChatMessages.Add(message);
StateHasChanged();
return Task.CompletedTask;
}
We mark the AddChatMessage
method with the JSInvokable
attribute. We need to add the JSInvokable
attribute to any method that we want to make available to JavaScript. We can provide the function name to the attributes’ string argument.
Also, we need the method we want to access from JavaScript to be a public
method.
In the method implementation, we add the received message to the ChatMessages
property using its Add
method. And we return a completed `Task.
Tip: If we use
async
methods for JavaScript interoperability, we support both cases, Blazor Server and Blazor WebAssembly. Therefore, it’s best practice to useasync
methods for JavaScript interoperability in Blazor.
We now have the method that creates the reference to the .NET object, and the method we want to call from JavaScript. The last method we need, is the Dispose
method.
public void Dispose()
{
GC.SuppressFinalize(this);
if (DotNetReference != null)
{
DotNetReference.Dispose();
}
}
In the Dispose
method, we call the GC.SuppressFinalize
method, and if there is a reference inside the DotNetReference
property, we call its Dispose
method.
Hint: To avoid a memory leak, it’s important to implement the
Dispose
method and call it on properties or variables of typeDotNetObjectReference
to free resources when the Blazor component is disposed of.
The JavaScript Code
Now that we have the Blazor component and implemented all three required C# methods, we have to implement the referenced JavaScript code.
Remember, we call a generateChatMessages
JavaScript function inside the OnAfterRender
method.
We add a scripts.js
file in the wwwroot
folder of the project and add a reference to this JavaScript file in the App.razor
file:
<body>
<Routes />
<script src="_framework/blazor.web.js"></script>
<!-- references the script.js file from the wwwroot folder -->
<script src="script.js"></script>
</body>
Inside the script.js
file, we add the following generateChatMessages
JavaScript function:
function generateChatMessages(dotnetObjectReference) {
dotnetObjectReference.invokeMethodAsync("AddMessage", "Hello from JavaScript!")
setTimeout(() => {
dotnetObjectReference.invokeMethodAsync("AddMessage", "Are you still there?!")
}, 2000);
setTimeout(() => {
dotnetObjectReference.invokeMethodAsync("AddMessage", "Blazor and JavaScript live side-by-side!")
}, 5000);
}
The function accepts a single parameter, the reference to the .NET object that we pass when calling the JavaScript function.
We then use the invokeMethodAsync
and provide the method name as the first parameter and the method argument as the second parameter. In our case, it’s the chat message.
We first send a chat message that should be displayed as soon as the application launches. Then we have two more messages that we send after a short period. We use the setTimeout
function and provide two and five seconds as the timeout period.
Now that everything is in place, we build and run the application and we should see the chat messages appear in the browser one-by-one. After five seconds have passed, the website should look like this:
Calling a C# Method Returning a Value
Now that we have our example in place, we want to step it up and not only call .NET code from a JavaScript function but also receive data from a C# method.
We add a GetName
method to the Home
component.
[JSInvokable]
public Task<string> GetName()
{
return Task.FromResult("Claudio Bernasconi");
}
Again, we use a Task
as the return type of a method that we want to call from JavaScript. Again, we use the JSInvoke
attribute to make it accessible to JavaScript.
This time, we do not provide an argument to the JSInvoke
attribute. This means we now have to use the GetName
method name to access the method from JavaScript.
We return my name as the static return value of type string
.
In the script.js
file, we add another setTimeout
statement to the generateChatMessages
function:
setTimeout(async () => {
const name = await dotnetObjectReference.invokeMethodAsync("GetName")
dotnetObjectReference.invokeMethodAsync("AddMessage", "Your name is: " + name + ".")
}, 3000);
After three seconds, we use the invokeMethodAsync
to call the GetName
method. We then use the return value of that method and pass it as an argument to an additional call to the AddMessage
method.
As a result, the name defined in the C# code will be sent to the JavaScript function and then sent back to the .NET code and displayed as another chat message inside the Home
component.
Let’s build and run the application one more time.
This time, after a few seconds, we see four chat messages appearing in the Blazor web application.
Conclusion
There are far more advanced JavaScript to .NET code interoperability options, such as returning arrays and calling static methods, etc.
However, the examples shown in this article are the fundamental interop solutions I have used most in my Blazor applications, and I believe those are the most common scenarios.
We learned how to call a C# method from a JavaScript function and we learned how to use return values (from a C# method) in JavaScript code.
Although I have used Blazor Server interactivity in this example, you can also use Blazor WebAssembly interactivity using the same JavaScript interoperability approach.
You can access the code used in this example on GitHub.
If you want to learn more about Blazor development, you can watch my free Blazor Crash Course on YouTube. And stay tuned to the Telerik blog for more Blazor Basics.
This content originally appeared on Telerik Blogs and was authored by Claudio Bernasconi
Claudio Bernasconi | Sciencx (2024-08-21T15:15:24+00:00) Blazor Basics: Blazor JavaScript Interop—Calling .NET from JavaScript. Retrieved from https://www.scien.cx/2024/08/21/blazor-basics-blazor-javascript-interop-calling-net-from-javascript/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.