Blazor University Learn the .NET SPA framework from Microsoft
Ctrl X

I am available for contract work, contact me via Linked-in or Email.

Calling JavaScript from .NET

JavaScript should be added into either /Pages/_Host.cshtml in Server-side Blazor apps, or in wwwroot/index.html for Web Assembly Blazor apps.

Our JavaScript can then be invoked from Blazor by injecting the IJSRuntime service into our component.

public interface IJSRuntime
{
  ValueTask<TValue> InvokeAsync<TValue>(string identifier, object[] args);
  ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object[] args);
  // Via an extension class
  void InvokeVoid(string identifier, params object[] args);
}

The identifier must be a JavaScript function scoped to the global window variable, but it is not necessary to include window in the identifier. So, to invoke window.alert we only need to specify alert as the identifier.

@page "/"
@inject IJSRuntime JSRuntime

<button @onclick=ButtonClicked>Perform interop</button>

@code
{
   private async Task ButtonClicked()
   {
     await JSRuntime.InvokeVoidAsync("alert", "Hello world");
   }
}

Passing parameters

The previous example passed the string "Hello world" as a parameter to the JavaScript alert function. It is also possible to pass complex objects to JavaScript. Parameters are serialized to JSON and then deserialized in JavaScript before being passed by-value as an anonymous object type to the function being invoked.

All parameter types passed to JavaScript must be basic types (string / int / etc) or be JSON serializable.

Create a new Blazor application, and add the following Person class.

using System.Collections.Generic;

namespace PassingComplexObjectsToJavaScript.Models
{
  public class Person
  {
    public string Salutation { get; set; }
    public string GivenName { get; set; }
    public string FamilyName { get; set; }
    public List<KeyValuePair<string, string>> PhoneNumbers { get; set; }

    public Person()
    {
      PhoneNumbers = new List<KeyValuePair<string, string>>();
    }
  }
}

Now in our Index.razor page create an instance of Person and pass it to console.log.

@page "/"
@inject IJSRuntime JSRuntime

<button @onclick=ButtonClicked>Perform interop</button>

@code
{
  private async Task ButtonClicked()
  {
    var person = new Models.Person
    {
      Salutation = "Mr",
      GivenName = "Chuck",
      FamilyName = "Morris"
    };
    person.PhoneNumbers.Add(new KeyValuePair<string, string>("Home", "+44 (0)121 999 9999"));
    person.PhoneNumbers.Add(new KeyValuePair<string, string>("Work", "+44 (0)161 999 9999"));

    await JSRuntime.InvokeVoidAsync("console.log", "Hello", person);
  }
}

If we view the browser's console output we should see the following when the button is clicked.

Accessing JavaScript return values

So far we've only used the IJSRuntime extension method InvokeVoidAsync. If we want to receive the return value from a JavaScript function we need to use the InvokeAsync<TValue> method. In the following example we'll invoke the standard JavaScript confirm function (which returns a Boolean) and the prompt function (which returns a string).

@page "/"
@inject IJSRuntime JSRuntime

<p>
  Status: @Result
</p>
<p>
  <button @onclick=ShowConfirm>Confirm popup</button>
</p>
<p>
  <button @onclick=ShowPrompt>Prompt popup</button>
</p>

@code
{
  private string Result;

  private async Task ShowConfirm()
  {
    bool confirmed = await JSRuntime.InvokeAsync<bool>("confirm", "Are you sure?");
    Result = confirmed ? "You clicked OK" : "You clicked Cancel";
  }

  private async Task ShowPrompt()
  {
    string name = await JSRuntime.InvokeAsync<string>("prompt", "What is your name?");
    Result = "Your name is: " + name;
  }
}