Initializing Web Components in Blazor via JS Interop

This blog post shows how to use a simple JavaScript web component in a Blazor app. The web component used here is a friend-list component that takes a friend array. A friend has firstName and lastName and the friend-list component displays the friends in an unordered HTML list.

There are different ways how a web component could take such a complex object like a friend array. In this blog post we look at two friend-list components that use the following ways:

  1. Via attribute taking a JSON string
  2. Via property taking an object

The goal of this post is not to explain web components. The goal is to learn how to use such a web component in a Blazor app.

I’ve created a GitHub repo that shows both ways for the friend-list web component. The repo also shows how to integrate the friend-list web component in a Blazor app via JSInterop. It contains all the code of this blog post and you find it here:

https://github.com/thomasclaudiushuber/Blazor-Using-Custom-Web-Component

The repo contains for each of the two cases a friend-list web component and a Blazor app that uses that web component.

Let’s start with the case of the “JSON string via attribute”.

Case 1: Web component takes a JSON string via attribute

You find that case here in the repo: https://github.com/thomasclaudiushuber/Blazor-Using-Custom-Web-Component/tree/master/UsingJsonString

The created friend-list component can be used in pure HTML like this:

<friend-list friendsJson='[ { "firstName":"Thomas","lastName":"Huber"},
  {"firstName":"Gill","lastName":"Cleeren"}]'>
</friend-list>

The friend-list web component uses the JSON string to display a list of friends with <ul><li> elements in HTML. The rendered UI of the web component looks like this:

The rendered friend-list web component

In the Blazor app I tried to use the friend-list web component in the Index.razor component. I tried to initialize its friendsJson property via data binding. But I noticed it does not work, especially the web components connectedCallback seemed not to work due to this open issue: https://github.com/dotnet/aspnetcore/issues/6218

So I started to go the JavaScript interop way.

If something doesn’t work natively in Blazor, you can always use JavaScript interop!

Let’s look at this.

I used the friend-list web component in the Index.razor component like this:

@page "/"
@using BlazorWebComp.Data
@inject IJSRuntime JSRuntime

<h1>Hello, world!</h1>

<friend-list @ref="friendList"></friend-list>


@code{

    private ElementReference friendList;

    private Friend[] friends = new[]
    {
        new Friend{FirstName="Thomas Claudius",LastName="Huber"},
        new Friend{FirstName="Gill",LastName="Cleeren"},
    };

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JSRuntime.InvokeVoidAsync("jsInterop.initializeFriendList", friendList, friends);
        }
    }
}

As you can see, at the top the IJSRuntime is injected and stored in the JSRuntime property. In the OnAfterRenderAsync method the InvokeVoidAsync method of the IJSRuntime is used to call the jsInterop.initializeFriendList JavaScript function.

I pass in the friendList-ElementReference. That element reference points to the friend-list web component, because the @ref attribute is set on the web component to that friendList field. I also pass in the friends array that I created in the Blazor component. That .NET Friend-array passed to the JavaScript function via IJSRuntime is automatically converted to a JavaScript object.

The JavaScript function initializeFriendList is defined in a separate JavaScript file that I’ve included in the _Host.cshtml file (or in Blazor WASM you would use the index.html file). The JavaScript function is defined like below:

var jsInterop = jsInterop || {};

jsInterop.initializeFriendList = function (friendListElement, friends) {
    friendListElement.friendsJson = JSON.stringify(friends);
};

The initializeFriendList function sets the friendsJson property of the friend-list web component to the JSON string of the friends-array parameter by using the JSON.stringify function. You could create that JSON string also in .NET. But I prefer to do it in JavaScript, because this means that the Index.razor component doesn’t have to take care of it und it can just use a .NET Friend-array (and we can even keep the Index.razor component unchanged for case 2 described in the next section).

At runtime, the Friend-Array is shown by the friend-list web component that is used in our Blazor app. That’s it. Everything just works.

The Blazor app using the friend-list web component

Now let’s look at the second case that I mentioned at the beginning of this blog post.

Case 2: Web component takes an object via property

It is the case where the the friend-list web component takes not a JSON string, but a complex object. That case is in this folder of the git repo: https://github.com/thomasclaudiushuber/Blazor-Using-Custom-Web-Component/tree/master/UsingObject

You cannot assign the complex object via attribute, but via property. In pure JavaScript, you would use that web component for example like this:

<body>
    <friend-list id="myFriendList">
    </friend-list>
    <script>
        var friendList = document.getElementById('myFriendList');
        friendList.friends = friends = [{
            firstName: "Thomas",
            lastName: "Huber"
        }, {
            firstName: "Gill",
            lastName: "Cleeren"
        }];
    </script>
</body>

In the Blazor app, everything looks the same. So, exactly the same Index.razor component like above with the JSRuntime interop code. But you would adjust the JavaScript function to initialize the web component to access that friends property. No JSON.stringify call is needed here, instead the friends parameter containing the friend-array is used directly for the friends-property of the friend-list component:

var jsInterop = jsInterop || {};

jsInterop.initializeFriendList = function (friendListElement, friends) {
    friendListElement.friends = friends;
};

Bonus: Wrap the web component in a native Razor/Blazor component

Now, of course, instead of doing this @ref and JSInterop everywhere in your Blazor app where you use that friend-list web component, it is in my opinion a much better way to wrap your custom web component in a native Razor/Blazor component. That’s the power of Blazor!

That native Blazor component would have a Friends property, in my case of type Friend[], and it does the JS Interop for you behind the scenes. The git repo contains also a folder that covers this case. You find it here:

https://github.com/thomasclaudiushuber/Blazor-Using-Custom-Web-Component/blob/master/_BlazorNativeWrapperComponent

The Blazor app in that folder contains a native Razor component called FriendList that has the content below:

@using BlazorWebComp.Data
@inject IJSRuntime JSRuntime

<friend-list @ref="friendList"></friend-list>

@code{
    private ElementReference friendList;

    [Parameter]
    public Friend[] Friends { get; set; }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await InitWebComponentAsync();
        }
    }

    private async Task InitWebComponentAsync()
    {
        await JSRuntime.InvokeVoidAsync("jsInterop.initializeFriendList", friendList, Friends);
    }
}

As you can see, it contains the whole JSInterop stuff and it has a parameter property called Friends.

The Index.razor component of that Blazor app uses the FriendList component that wraps the friend-list web component. It’s now as simple as this:

@page "/"
@using BlazorWebComp.Data

<h1>Hello, world!</h1>

<FriendList Friends="friends"></FriendList>

@code{
    private Friend[] friends = new[]
{
        new Friend{FirstName="Thomas Claudius",LastName="Huber"},
        new Friend{FirstName="Gill",LastName="Cleeren"},
    };
}

I hope this helps you to integrate other web components in your Blazor application.

Happy coding,
Thomas

PS: If you haven’t checked it out yet: There’s a Blazor learning path at Pluralsight: https://www.pluralsight.com/paths/building-web-applications-with-blazor .

Share this post

Comments (3)

  • Dew Drop – February 18, 2020 (#3135) | Morning Dew Reply

    […] Initializing Web Components in Blazor via JS Interop (Thomas Claudius Huber) […]

    February 18, 2020 at 2:14 pm
  • Dave Slinn Reply

    Is it possible to create a javascript web component from a razor component? Or is the fact that a razor component is published as a DLL make it impossible?

    November 12, 2020 at 10:18 am
    • Thomas Claudius Huber Reply

      Hi Dave,
      that’s not possible, as the razor component uses .NET and web assembly. It’s not compiled down to native JavaScript or WASM code. But this doesn’t mean that this could not happen in the future. Right now, it’s not possible to create a JS web component from a Razor component.

      Thank you,
      Thomas

      November 23, 2020 at 10:10 am

Leave a Reply to Dew Drop – February 18, 2020 (#3135) | Morning Dew Cancel reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.