19
- July
2020
Posted By : Rahul
Top 20 Blazor Questions and Answers – with Code Snippets

In this post, we will look into Top 20 Blazor Questions and Answers with code snippets that been most asked on StackOverFlow and also a nice collection for Blazor newbies and developers. Not only questions, but we will look their answers with small code snippets that will be helpful in you day to day development.

Since Blazor is awesome 😍 for C# developers and I love this framework, so I am posting only Blazor articles these days and today I have written this small collection of top 20 Blazor questions and their answers with code examples explained. Most of them questions are asked on StackOverFlow. You will get such more posts in my future posts.

Table of Questions

1. Is Microsoft Blazor Framework faster than other SPA frameworks for example Angular, React or Vue compiled in JavaScript?
2. What is difference between Blazor WebAssembly App (with Asp.Net Core Hosted) and Blazor Server App?
3. How can I access the browser’s localStoarge in Blazor?
4. How to store Session data in Blazor application?
5. How to debug Blazor WebAssembly application?
6. How to get current URL in a Blazor application?
7. How to Preserve State in Server-side Blazor application?
8. How to get IPAddress and UserAgent in Blazor server application?
9. How to download file in Blazor application?
10. How to disable anchor <a> link’s href disable if there is an onclick method on it in Blazor? OR How to use preventDefault (just like in JavaScript) to prevent the default action for an event in Blazor?
11. How to stop event propagation or event bubbling in Blazor?
12. How to pass arguments to @on{EVENT} handler in Blazor?
13. How to create Custom InputSelect element in Blazor which supports System.Guid or any data type?
OR
How to fix the error “System.InvalidOperationException: Microsoft.AspNetCore.Components.Forms.InputSelect`1[System.Guid] does not support the type ‘System.Guid”’?

14. How to show Confirm box before deleting record in Blazor?
15. How to Pass data from Parent component to Child Component in Blazor?
16. How to Pass data from Child component to Parent Component in Blazor?
17. What is JavaScript Interoperability (JS interop)?
18. How to call JavaScript functions from .NET methods in Blazor?
19. How to call C# static methods from JavaScript in Blazor?
20. How to call C# instance methods from JavaScript in Blazor?

Q1. Is Microsoft Blazor Framework faster than other SPA frameworks for example Angular, React or Vue compiled in JavaScript?

Blazor uses web assembly, On paper web assembly should be faster than any JavaScript library, however not all browsers have a mature web assembly parser yet. So you might find that browsers will not run web assembly in an optimal speed as of now.

You can create a small blazor app and run it in Firefox, chrome or edge. In most cases Firefox runs blazor apps much faster than chrome or edge, which implies that browser makers still need to improve, even Firefox can improve.

If your app needs to access DOM frequently, then definitely web assembly / Blazor will be slower compared to any JS libraries since web assembly can’t directly access DOM.

By itself, WebAssembly cannot currently directly access the DOM; it can only call JavaScript, passing in integer and floating point primitive data types. Thus, to access any Web API, WebAssembly needs to call out to JavaScript, which then makes the Web API call.

Check out here for more detail.

Additionally, current implementation Blazor has its own MSIL engine on top of the browsers web assembly Engine, which means there are two interpreters working to run a Blazor project, Like two translators interpreting a conversation instead on one. Currently Microsoft is working on an AOT compiler, which is not yet release. Once its release Blazor will be much faster than the current implementation.

Check out this link for more updates on Blazor AOT compilation.

We can safely assume that the web assembly is the future of web development, but at the moment we can’t say anything about Blazor’s future. On paper Blazor can be faster than any framework out there, however we need commitment from web assembly maintainers, Browser developers, Microsoft and the communities to make the theories practical.

Reference link of this answer.

Q2. What is difference between Blazor WebAssembly App (with Asp.Net Core Hosted) and Blazor Server App?

There are two hosting models: Server-Hosted, and Client-Hosted. The difference is whether the app is hosted in server, or in client.

Server-Hosted Model

Server hosted model means your app logic runs in the server (you can think of it similar to what Web Forms is), you click on a button, an “Ajax” call sends the request, the server receives the request, and sends back the updated page. However, here it uses SignalR not Ajax, which is a low level socket communication (read efficient). And instead of updating a whole page, it updates only the relevant parts (thus it is a single page application).

Client-Hosted Model

Client hosted model means your logic runs within the browser. Think of it as if your C# logic is converted into JavaScript, and it is embedded in the page. So the logic runs in the browser. This could be possible after the introduction of WebAssembly.

WebAssembly App (with Asp.Net Core Hosted)

This options means having Blazor to include ASP.NET Core runtime. This is because you can write an offline app (e.g. calculator app) that does not need any kind of connection to external services, making ASP.NET Core irrelevant. However, you might want to write an online app that accesses online DB, external APIs, do verification, etc. For these kind of apps, you will need an ASP.NET Core stack to support your app.

Reference link of this answer.

Q3. How can I access the browser’s localStoarge in Blazor?

There are couple of options available to achieve this functionality. But I will explain two best approaches here.

Approach #1

You can use Blazored.LocalStorage awesome library by Chris for accessing localStorage in Blazor applications. It can be installed from NuGet via Visual Studio Package manager or using below command –

> Install-Package Blazored.LocalStorage

How to Setup

For using in Blazor Server project, You will need to register the local storage services with the IServiceCollection under ConfigureServices method in Startup.cs file.

public void ConfigureServices(IServiceCollection services)
{
    services.AddBlazoredLocalStorage();
}

For using in Blazor WebAssembly project, You will need to register in Program.cs file.

public static async Task Main(string[] args)
{
    var builder = WebAssemblyHostBuilder.CreateDefault(args);
    builder.RootComponents.Add<App>("app");
    builder.Services.AddBlazoredLocalStorage();
    await builder.Build().RunAsync();
}

How to Use

To use it wither in Blazor WebAssembly project or in Blazor Server project, you will need to inject the ILocalStorageService –

@inject Blazored.LocalStorage.ILocalStorageService localStorage

@code {
     protected override async Task OnInitializedAsync()
     {
       //For Setting values
        await localStorage.SetItemAsync("website_name", "JustDebug.Net");
        //For getting values
        var websiteName = await localStorage.GetItemAsync<string>("website_name");
    }
}

There is another option of synchronous API to use local storage in Blazor WebAssembly project only. You can use ISyncLocalStorageService instead of ILocalStorageService that allows you to avoid use of async/await.

@inject Blazored.LocalStorage.ISyncLocalStorageService localStorage

@code {
     protected override void OnInitialized()
     {
         //For Setting values
        localStorage.SetItem("website_name", "JustDebug.Net");
        //For getting values
        var websiteName = localStorage.GetItem<string>("website_name");
    }
}

You can check it on GitHub for more understanding about this package.

Approach #2

There is another NuGet package provided by Microsoft Blazor Team named Microsoft.AspNetCore.ProtectedBrowserStorage that provides Data Protection for localStoarge and sessionStorage.

Important – Microsoft.AspNetCore.ProtectedBrowserStorage is an unsupported experimental package unsuitable for production use at this time. It may be in future.

You can check here more in detail about this package.

Q4. How to store Session data in Blazor application?

You can use another NuGet library called Blazored.SessionStorage by Chris for providing access to session storage in Blazor applications. It can be installed from NuGet via Visual Studio Package manager or using below command –

> Install-Package Blazored.SessionStorage

How to Setup

For using in Blazor Server project, You will need to register the local storage services with the IServiceCollection under ConfigureServices method in Startup.cs file.

public void ConfigureServices(IServiceCollection services)
{
    services.AddBlazoredSessionStorage();
}

For using in Blazor WebAssembly project, You will need to register in Program.cs file.

public static async Task Main(string[] args)
{
    var builder = WebAssemblyHostBuilder.CreateDefault(args);
    builder.RootComponents.Add<App>("app");
    builder.Services.AddBlazoredSessionStorage();
    await builder.Build().RunAsync();
}

How to Use

To use it wither in Blazor WebAssembly project or in Blazor Server project, you will need to inject the ISessionStorageService

@inject Blazored.SessionStorage.ISesisonStorageService sessionStorage

@code {
protected override async Task OnInitializedAsync()
{
       //For Setting values
        await sessionStorage.SetItemAsync("website_name", "JustDebug.Net");
        //For getting values
        var websiteName = await sessionStorage.GetItemAsync<string>("website_name");
    }
}

There is another option of synchronous API to use local storage in Blazor WebAssembly project only. You can use ISyncSessionStorageService instead of ISessionStorageService that allows you to avoid use of async/await.

@inject Blazored.SessionStorage.ISyncSessionStorageService sessionStorage

@code {
protected override void OnInitialized()
{
         //For Setting values
        sessionStorage.SetItem("website_name", "JustDebug.Net");
        //For getting values
        var websiteName = sessionStorage.GetItem<string>("website_name");
    }
}

You can check here more in detail about this package.

Q5. How to debug Blazor WebAssembly application?

To enable debugging for a Blazor WebAssembly app, follow the below steps –

Step 1. Update the launchSettings.json file with the following highlighted “inspectUri” field and value.

"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}"

Once updated, the launchSettings.json file should look similar to the following code –

how to debug blazor webassembly application

Step 2. Set the breakpoints in the any razor component C# code section (or in partial class). For example. In the following example, I have set on the Increment method.

debugging blazor web assembly put breakpoint

Step 3. Press F5 to run the app and navigate to the Counter tab and click the “Click Me” button to hit the break point.

debugging blazor webassembly app hit breakpoint

For more details on debugging and using Visual Studio Code, check out the official link.

Q6. How to get current URL in a Blazor application?

You can use Uri property of NavigationManager class to get the current URL.

How to use

Inject NavigationManager class using @inject directive into razor component or using [Inject] attribute, if using code-behind approach of creating components.

Check out my article here to know about 4 different methods of creating Blazor components.

To inject inside component, use @inject directive like below –

@inject NavigationManager navigationManager

To inject NavigationManager in code-behind file, use [Inject] attribute like below –

[Inject]
Public NavigationManager AppNavigationManager {get; set;}

Now you have injected NavigationManager, so you need to Uri property to get current URL.

@inject NavigationManager navigationManager

<p>Current URL:  @(navigationManager.Uri) </p>

To know more about NavigationManager, check out this official link.

Q7. How to Preserve State in Server-side Blazor application?

You can find the best answer of this question from here given by Steve Sanderson (Creator of Microsoft Blazor Framework).

Q8. How to get IPAddress and UserAgent in Blazor server application?

You can get user information using IHttpContextAccessor in Blazor server app.

  • Add the following line in the ConfigureServices method inside Startup.cs file.
services.AddHttpContextAccessor();
  • Inject IHttpContextAccessor in blazor component using @inject directive or using [Inject] attribute in code-behind file.
@inject Microsoft.AspNetCore.Http.IHttpContextAccessor _httpContextAccessor
  • Get UserAgent and IPAddress value using _httpContextAccessor object
<h3>User Agent : @_httpContextAccessor.HttpContext.Request.Headers["User-Agent"].ToString()</h3>

<h3>User IPAddress : @_httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString()</h3>

Using JavaScript Interop

To get UserAgent information using JavaScript Interop, follow below steps –

  • Add a JavaScript file under wwwroot folder named “userinfo.js” and add the following code
window.getUserAgent = function () {
    return this.navigator.userAgent;
}
  • Add JS file in _Host.cshtml file (in case of Blazor server) OR in index.cshtml file (in case of Blazor WebAssembly) just before the body closing tag
<script src="js/userinfo.js"></script>
  • Now to invoke a JS function from any Blazor component, Inject IJSRuntime using @inject directive
@inject IJSRuntime jsruntime
  • Now you can invoke JS function and get the UserAgent information here in component using following code
public async Task GetUserAgent() {
     var ua = await jsruntime.InvokeAsync<string>("getUserAgent");
}

Q9. How to download file in Blazor application?

You can use JSInterop feature for downloading file in Blazor application. Use the following steps to do this work –

  • Create a JavaScript file named “download.js” under wwwroot folder and add the following code to it
window.saveAsFile = function (fileName, byteBase64) {
    var link = this.document.createElement('a');
    link.download = fileName;
    link.href = "data:application/octet-stream;base64," + byteBase64;
    this.document.body.appendChild(link);
    link.click();
    this.document.body.removeChild(link);
}
  • Add this JS file in _Host.cshtml file in Blazor Server app or in Index.html file in Blazor WebAssembly app just before the body closing tag
<script src="js/download.js"></script>
  • Now invoke saveAsFile JavaScript function inside any Blazor component using IJSRuntime
@inject IJSRuntime jsruntime

<button @onclick="DownloadFile">Download File</button>

@code{
    protected async Task DownloadFile()
    {
        var fileName = "myfile.txt";
        var text = "Hello Blazor, I'm your fan !!";
        var bytes = System.Text.Encoding.UTF8.GetBytes(text);
        await jsRuntime.InvokeAsync<object>("saveAsFile", fileName, Convert.ToBase64String(bytes ));
    }
}
  • Now click the Download File button to download the file 😀

Reference link of this answer.

Q10. How to disable anchor <a> link’s href disable if there is an onclick method on it in Blazor?
OR
How to use preventDefault (just like in JavaScript) to prevent the default action for an event in Blazor?

You can use @on{EVENT}:preventDefault directive attribute to prevent the default action for an event.

For example, I have an anchor link with href attribute and an onlick event handler in a Blazor component.

<a href="https://www.google.com" @onclick="doClick">Click Me</a>

When you will run the Blazor application and click on this anchor link, you will be redirected to google.com along with the doClick handler execution.

Solution -: To stop navigation (redirection to href link), you need to use @onclick:preventDefault directive attribute like below –

<a href="https://www.google.com" @onclick="doClick" @onclick:preventDefault>Click Me</a>

Now you won’t be redirected to any where.

Q11. How to stop event propagation or event bubbling in Blazor?

You can use @on{EVENT}:stopPropagation directive attribute to stop event propagation in Blazor.

For Example, I have multiple nested <div> elements (each with onclick event) or multiple <button> elements with onclick event nested inside a <div> element in a Blazor component. In the following example, I am showing this with a single <button> element nested inside a <div> element, where <div> and <button> both elements have onclick event.

<p>Current count: @currentCount</p>

<div @onclick="divClickIncrementCount" style="border-width: 5px; border-color: green; border: solid 5px green;">
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
</div>

@code {
    private int currentCount = 0;
    private void IncrementCount()
    {
        currentCount++;
    }
    private void divClickIncrementCount()
    {
        currentCount++;
    }
}

When you run the blazor application and click on button, button onclick handler (IncrementCount method) along with parent <div> element onclick handler (divClickIncrementCount method) will be executed that means currentCount value will be incremented twice with a single button click.

Event Bubbling means When an event happens on an element, it first runs the handler on it, then on its parent, then all the way up on other ancestors.

Solution -: To stop event bubbling, you need to use @onclick:stopPropagation=”true” directive attribute in Blazor. This way currentCount value will be increment on individual click on child and parent element.

<div @onclick="divClick" style="border-width: 5px; border-color: green; border: solid 5px green;">
    <button class="btn btn-primary" @onclick="IncrementCount" @onclick:stopPropagation="true">Click me</button>
</div>

Q12. How to pass arguments to @on{EVENT} handler in Blazor?

 For passing argument to a handler of an event in Blazor, you can use lambda.

For example. If you have a button in a Blazor component and you want to pass arguments to its onClick (or onmouseover or any event) handler like below –

<button @onclick="OnButtonClick(5, 5)">Click Me</button>

This code won’t work because we’re just binding the onclick event with the returning result of OnButtonClick method. Rather we need to attach method itself with the help of lambda.

<button @onclick="@(e=> OnButtonClick(5, 5))">Click Me</button>

<button @onmouseover="@(e=> OnButtonMouseOver(10, 15))">Hover Me</button>

Now run the application and you can get both argument values inside the handler.

public int Sum { get; set; }
protected void OnButtonClick(int val1, int val2)
{
    Sum = val1 + val2;
}

Q13. How to create Custom InputSelect element which supports System.Guid or any data type in Blazor?

OR

How to fix the error “System.InvalidOperationException: Microsoft.AspNetCore.Components.Forms.InputSelect`1[System.Guid] does not support the type ‘System.Guid”’?

By default, InputSelect Blazor control supports only String and Enum data type.  For supporting other data types, you need to create your custom InputSelect control.

Replicating the issue with existing InputSelect element

First of all, let implement a simple example in which we will a EmployeeList with two properties Id (System.Guid) and Name (String) and we will this list of employees with existing InputSelect element.

Create a model class called Employee with two properties.

public class Employee
{
        public Guid EmployeeId { get; set; }
        public string EmployeeName { get; set; }
}

Now create a Blazor component called BindGuidToInputSelect.razor and add the following code into the component.

<h3>Custom InputSelect supports GUID</h3>

<EditForm Model="@SelectedEmployee">
    <div class="form-group row">
        <label for="country" class="col-sm-2 col-form-label">Country</label>
        <div class="col-sm-10">
            <InputSelect id="country" @bind-Value="SelectedEmpId" class="form-control">
                @foreach (var c in Employees)
                {
                    <option value="@c.EmployeeId">@c.EmployeeName</option>
                }
            </InputSelect>
        </div>
    </div>
</EditForm>

<button @onclick="ChangeEmployee">Change Country</button>

<p>Employee Selection: @SelectedEmployee.EmployeeId | @SelectedEmployee.EmployeeName</p>
@code {
    public Employee SelectedEmployee { get; set; } = new Employee();
    public List<Employee> Employees { get; set; } = new List<Employee>();
    public Guid SelectedEmpId { get; set; }

    protected async override Task OnInitializedAsync()
    {
        //EmployeeList is returning from EmployeeService
        Employees = await EmployeeService.GetEmployees();
    }
   
    protected void ChangeEmployee()
    {
        var emp = Employees.Where(r => r.EmployeeId == SelectedEmpId).FirstOrDefault();
        if (emp != null)
        {
            SelectedEmployee = emp;
        }
        StateHasChanged();
    }
}

In the above example, I have binded EmployeeList to InputSelect element where EmployeeId (which is a GUID value) binds to its value and EmployeeName (which is a string value) for displaying names in the dropdown.

Let’s run the application and change the employee from InputSelect control. As you change selection, you will get the following exception –

Error: System.InvalidOperationException: Microsoft.AspNetCore.Components.Forms.InputSelect`1[System.Guid] does not support the type ‘System.Guid’ at Microsoft.AspNetCore.Components.Forms.InputSelect`1.TryParseValueFromString(String value, TValue& result, String& validationErrorMessage) at Microsoft.AspNetCore.Components.Forms.InputBase`1.set_CurrentValueAsString(String value).

   Reason of exception

   The reason of exception is that by default, InputSelect element does not support other data type than System.String. To support other data types, you need to create your own custom InputSelect.

Creating a Custom InputSelect

Create a class file named CustomInputSelect.s and add the following code to it.

public class CustomInputSelect<TValue> : InputSelect<TValue>
    {
        protected override bool TryParseValueFromString(string value, out TValue result, out string validationErrorMessage)
        {
            if (typeof(TValue) == typeof(Guid))
            {
                if (Guid.TryParse(value, out var resultGUID))
                {
                    result = (TValue)(object)resultGUID;
                    validationErrorMessage = null;
                    return true;
                }
                else
                {
                    result = default;
                    validationErrorMessage = $"The selected value {value} is not a valid GUID.";
                    return false;
                }
            }
            else if (typeof(TValue) == typeof(int))
            {
                if (int.TryParse(value, out var resultInt))
                {
                    result = (TValue)(object)resultInt;
                    validationErrorMessage = null;
                    return true;
                }
                else
                {
                    result = default;
                    validationErrorMessage = $"The selected value {value} is not a valid Integer.";
                    return false;
                }
            }
            else
            {
                return base.TryParseValueFromString(value, out result, out validationErrorMessage);
            }
        }
    }

In the above code, CustomInputSelect class is inherited from InputSelect base type. I did the implementation of supporting of System.Guid & System.Int datatypes as of now. If you want to add more data type support, you can enhance the existing class.

Let’s change the InputSelect element with the CustomInputSelect element in the same example above explained.

<EditForm Model="@SelectedEmployee">
    <div class="form-group row">
        <label for="country" class="col-sm-2 col-form-label">Country</label>
        <div class="col-sm-10">
            <CustomInputSelect id="country" @bind-Value="SelectedEmpId" class="form-control">
                @foreach (var c in Employees)
                {
                    <option value="@c.EmployeeId">@c.EmployeeName</option>
                }
            </CustomInputSelect>
        </div>
    </div>
</EditForm>

Now run the application and try changing employee from the dropdown.

How to create Custom InputSelect in Blazor

You can check out of the box Blazor InputSelect supported datatype and source code using this link.

Q14. How to show Confirm box before deleting record in Blazor?

You can achieve this functionality using JavaScript Interop feature. Let’s see an example how we can achieve that.

Create a component called EmployeeList.razor just to display employees and add the following code.

EmployeeList.razor

@page "/employeelist"
@inject IJSRuntime JsRuntime 

<h3>Confirm before Deleting Employee</h3>

<table class="table table-striped">
    <thead>
        <tr>            
            <th scope="col">Name</th>
            <th scope="col"></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var emp in Employees)
        {
            <tr>
                <td>@emp.EmployeeName</td>
                <td><button @onclick="@(e=> DeleteEmployee(emp.EmployeeId))">Delete</button></td>
            </tr>
        }
    </tbody>
</table>
@code {
    public List<Employee> Employees { get; set; } = new List<Employee>();

    protected async override Task OnInitializedAsync()
    {
        Employees = await EmployeeService.GetEmployees();
    }

    public async Task DeleteEmployee(Guid Id)
    {
        bool confirmed = await JsRuntime.InvokeAsync<bool>("confirm", "Are you sure to delete this employee?");
        if (confirmed)
        {
            var emp = Employees.Where(r => r.EmployeeId == Id).FirstOrDefault();
            if (emp != null)
            {
                Employees.Remove(emp);
                StateHasChanged();
            }
        }
    }
}

In the above code, I fetched a list of employee from EmployeeService and displayed all employees on the page using Bootstrap table. There is a button called Delete, added with each record. Now when user will click to this button, a dialog will be open to confirm just before deleting that user.

This is JavaScript confirm dialog which has been called using IJSRuntime object in this component. Let’s run the application and see the output.

display-confirm-dailog-before-delete-in-blazor

Q15. How to Pass data from Parent component to Child Component in Blazor?

For passing data from parent component to child component, there are mainly three methods in Blazor –

Using Parameters

A component can receive data from its parent component using [Parameter] attribute. Meaning You can use component [Parameter] attribute to pass data from parent to child component in Blazor.

For a deeper look, check out  my detailed article here where you can get about component parameters in brief.

Using Cascading Value and Parameters

Blazor introduced another way for passing data from parent to child components.

For example. Component C can be nested inside component B and that component B can be nested inside another component A and so on. It means there can be several layers of components in a hierarchy in a real-time blazor application.

As I explained above, there is one way to pass data from an ancestor component (component A) to its descendant components (component B & C and more in hierarchy) using Component Parameters. But using this way, it’s tedious to pass data to all descendant components. That’s why Blazor introduced cascading values and parameters that allows any data cascaded down to its all descendant components in components hierarchy.

For a deeper look at Cascading Values and Cascading Parameters, check out my detailed article.

Using Route Parameters

You can pass string data as a route parameter from the parent component (navigating from) to child component (navigated to). In the page you are navigating to, you need to add a property (with the same name as route parameter) decorated with parameter attribute.

Suppose you want to pass data from component 1 to component 2, the you need to –

  • Inject NavigationManager class to the component 1 page
@Inject NavigationManager navManager
  • Call NavigateTo method of NavigationManager to move to the component 2 with name
navManager.NavigateTo("/component2/Rahul");
  • In component 2, add @page directive with route and parameter
@page "/component2/{myParam}"
  • Now add a property decorated with [Parameter] attribute
[Parameter]
Public string myParam {get; set; }
  • Now you can this parameter value in OnParameterSet lifecycle method
Protected override void OnParameterSet()
{
	var name = myParam;          //this will print Rahul
}

Q16. How to Pass data from Child component to Parent Component in Blazor?

 You can use EventCallBack in Blazor for passing data from child component to parent component. An EventCallBack is a special type of delegate which is used in a common scenario with nested components to execute a parent component method when a child component event occurs.

We can say that for passing any data from child component to parent component when any event is occurred inside child component, blazor provides us a special type of delegate called EventCallBack.

For detailed guide about EventCallBack, check out my article here.

Q17. What is JavaScript Interoperability (JS interop)?

A Blazor app can invoke JavaScript functions from .NET methods and .NET methods from JavaScript functions. This scenario is called JavaScript Interoperability (JS Interop).

Q18. How to call JavaScript functions from .NET methods in Blazor?

To call into JavaScript from .NET, use the IJSRunime abstraction. To invoke JS functions, you need to inject IJSRuntime into your Blazor component.

IJSRuntime has following two methods named InvokeVoidAsync and InvokeAsync for calling JavaScript functions from .NET. These methods take first parameter as identifier for JavaScript function.

  • InvokeVoidAsync – This method is used when JavaScript function doesn’t return any value.
  • InvokeAsync – This method is used when JavaScript function returns any value after execution.

Calling JS function that doesn’t return any value

Create a JavaScript file called JsInteropTest.js (you can give any name) and add the following code to file.

var commonservice = {
    doLog: function () {
        console.log("Hi there, I've been logged by doLog JS function called from Blazor.");
    }
};

Add this JS file to _Host.cshtml (in case of Blazor WebAssembly Project) file OR index.cshtml file (in case of Blazor Server project) just before the body closing tag.

<script src="scripts/JSInteropTest.js"></script>

Now add a Blazor component called CallingJSFunctionsFromDotnet.razor and add the following code –

@inject IJSRuntime JsRuntime

<h2>Call JS functions with no return value from .NET</h2>
<button class="btn btn-primary" @onclick="doLogCallback">Call JavaScript doLog function</button>

@code {
    public async void doLogCallback(MouseEventArgs e)
    {
        await JsRuntime.InvokeVoidAsync("commonservice.doLog");
    }
}

In the above example, I have used InvokeVoidAsync method of IJSRuntime because doLog JS function doesn’t return anything, just log a message to console.

Now run the application, click the button and open the developer tools window. You can check message is logged to the console.

Calling JS function that returns a value and accepts the arguments

Let use the same JS file and add the following code. In the following code, I am calling a JavaScript function that opens a JavaScript prompt window with a question. User just need to put answer and hit okay button.

JsInteropTest.js

commonservice.getPromptResult = function (question, defaultValue) {
    return prompt(question, defaultValue);
}

CallingJSFunctionsFromDotnet.razor

<button class="btn btn-primary" @onclick="callFunctionWhichReturnsValue">Call JavaScript function which returns value</button>

<p>Prompt Result: @promptResult</p>

@code {
    private string promptResult = string.Empty;

    public async Task callFunctionWhichReturnsValue(MouseEventArgs e)
    {
        var result = await JsRuntime.InvokeAsync<string>("commonservice.getPromptResult", "What's your name?", "");

        if (result != null)
        {
            promptResult = result;
        }
    }
}

As you can see, I have used InvokeAsync this time because JS function will return prompt result.

Run the application and hit the button. You will get a prompt with question “What’s your name?”. Put the answer and hit Okay button. You’ll get the result.

Calling JavaScript function that returns Object

You can return object also from JS function which you can get and deserialize in Blazor like below –

JsInteropTest.js

commonservice.getEmployeeAsObject= function (empID, empName) {
    return { employeeId: empID, employeeName: empName };
}

CallingJSFunctionsFromDotnet.razor

private BethanysPieShopHRM.Shared.Employee employee;

public async Task CallJSFunctionReturnsObject()
{
    employee = await JsRuntime.InvokeAsync<Employee>("commonservice.getEmployeeAsObject", "12345", "Rahul");
}

Employee.cs

public class Employee
{
       public int EmployeeId { get; set; }       
       public string EmployeeName{ get; set; }
}

Q19. How to call C# static methods from JavaScript in Blazor?

You can invoke .NET static methods from JavaScript in Blazor using DotNet.invokeMethod or DotNet.invokeMethodAsync functions.

Calling Static .NET methods from JavaScript

To invoke a static .NET method from JavaScript, use the DotNet.invokeMethod or DotNet.invokeMethodAsync functions. First argument passed into these method is name of assembly containing the .NET method and second argument is method name itself.

The .NET method being invoked from JavaScript must be public, static and decorated with [JSInvokable] attribute.

CallingDotnetMethodsFromJSFunctions.razor

[JSInvokable]
public static string GetEmailAddress(string name)
{
        return $"{name}@justdebug.net";
}

JsInteropTest.js

commonservice.getEmailAlert = function () {
    var result = DotNet.invokeMethodAsync("BlazorApp", "GetEmailAddress", "rahul");
    result.then(email => alert(email));
}

In above code, C# method is public, static and decorated with [JSInvokable] attribute and in JS code, DotNet.invokeMethodAsync function is used to invoke where first parameter passed as BlazorApp (which is assembly name), second argument passed as C# method name and third argument is passed a name value which is being return from C# code.

DotNet.invokeMethodAsync function returns a promise which will display returned email when promise is resolved.

Q20. How to call C# instance methods from JavaScript in Blazor?

You can also invoke .NET non-static or instance methods from JavaScript in Blazor. To invoke a .NET instance method from JavaScript, you need to pass the .NET instance by reference to JavaScript using DotNetObjectReference.Create method.

Calling Instance .NET methods from JavaScript

CallingDotnetMethodsFromJSFunctions.razor

System.Drawing.Size _windowSize;
public async void PassComponentInstanceToJS()
{
        var componentInstance = DotNetObjectReference.Create(this);
        await JsRuntime.InvokeVoidAsync("commonservice.callDotnetInstanceMethod", componentInstance);
}

[JSInvokable]
public void GetWindowSize(System.Drawing.Size windowSize)
{
        _windowSize = windowSize;
        StateHasChanged();
}

JsInteropTest.js

commonservice.callDotnetInstanceMethod = function (dotnetObjectReference) {
    dotnetObjectReference.invokeMethodAsync("GetWindowSize", {
        width: window.innerWidth,
        height: window.innerHeight
    });
}

In the above example, I have created an instance method in component called PassComponentInstanceToJS in which a JavaScript function named callDotnetnstanceMethod is invoked and passing current component instance to JS using DotNetObjectReference.Create(this) method. Since current component instance is passed to JS, so now I can call GetWindowSize method from JS, which is a .NET instance method inside current component.

Summary

In this post, we covered Top 20 coding Blazor questions and answers with code examples. There are many Blazor topics (like JS Interop in Blazor, Communication between Blazor components etc.) been covered with these questions.

Please share this post if you find it helpful. Thank you 🙏 for reading this article. Keep Reading, Keep Growing, Keep Sharing 😃

If you found this article helpful,

BMC logoBuy me a coffee

Leave a Reply