Telerik blogs

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 use async 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 type DotNetObjectReference 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:

A Blazor Server web application showing a title and three chat messages: Hello from JavaScript! Are you still there?! Blazor and JavaScript live side-by-side!

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.

A Blazor Server web application showing a title and four chat messages: Hello from JavaScript! Are you still there?! Your name is: Claudio Bernasconi. Blazor and JavaScript live side-by-side!

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.


About the Author

Claudio Bernasconi

Claudio Bernasconi is a passionate software engineer and content creator writing articles and running a .NET developer YouTube channel. He has more than 10 years of experience as a .NET developer and loves sharing his knowledge about Blazor and other .NET topics with the community.

Related Posts

Comments

Comments are disabled in preview mode.