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:
- Via attribute taking a JSON string
- 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:
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.
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:
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 .
Comments (3)
[…] Initializing Web Components in Blazor via JS Interop (Thomas Claudius Huber) […]
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?
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