Write a reusable Blazor component

First part of a Blazor article series to tell how to create a reusable Blazor component that will be used in your Blazor application independently of the hosting model (Web Assembly or Server-side).

Xavier Solau
YounitedTech
Published in
5 min readOct 28, 2020

--

You can find the Github source repository this article series in based on here.

The Blazor article series

Create the component project

To create a reusable component, we need to create a Razor Class Library with visual studio:

Or razorclasslib with dotnet command line interface:

dotnet new razorclasslib

By default, the created project contains several elements:

A predefined “Component1” that is a basic component displaying some styled text in Component1.razor.

  • A ExampleJsInterop class that demonstrate how to call a basic JS function defined in its own js file in ExampleJsInterop.cs.
  • The static resources (like png, css and js files) in the “wwwroot” folder:
    - background.png
    - exampleJsInterop.js
    - styles.css

Separate the code from the razor code

Behind the scenes, when you write a razor page, an actual C# class is generated and hopefully the class is partial. It means that it is easy to separate the behavioral code of your component from the html view code.

Single mixed file Component1.razor:

<div class="my-component">
<!-- Some View & Html code -->
</div>
@code {
// Some code.
}

And now the same with separated files for the view and the code:

  • one Component1.razor file for the view side
<div class="my-component">
<!-- Some View & Html code -->
</div>
  • and one Component1.cs file for the code part.
using System;
using System.Collections.Generic;
using System.Text;
namespace MySharedComponents
{
public partial class Component1
{
// Some code
}
}

Tips:

In Visual Studio, you can link the two files in the Solution Explorer view adding some lines in the csproj file.

<ItemGroup>
<Content Update=”Component1.razor” />
<Compile Update=”Component1.cs”>
<DependentUpon>Component1.razor</DependentUpon>
</Compile>
</ItemGroup>

Add some behaviors

We are going to modify the component to be able to exchange some data between the component and the parent and to use a JS function through the generated “ExampleJsInterop” to prompt the user for a text input.

The component will be used this way with “TextValue” being a property used in the binding :

<Component1
ParentName
=”Some text I want to transmit from the parent to the child component”
@bind-Text=”TextValue”>
</Component1>

Define the parameters

Component input.

We need to define a property “ParentName” to transmit some data from the parent to the child component:

  [Parameter]
public string ParentName { get; set; }

The “Parameter” attribute tells that the C# property can be used as component parameter.

Component input/output.

Now we need to define a parameter to be able to transmit data to the child component, but we also want to get data from the component using a “@bind” property.

To make it possible, we must:

  • Define a parameter like the previous one named in our case “Text”.
  [Parameter]
public string Text { get; set; }
  • Define a “EventCallback” parameter to be able to notify the binding engine that the value has changed. The name of the event callback property must be named accordingly to the property you want to bind. In our case it must be named “TextChanged.
  [Parameter]
public EventCallback<string> TextChanged { get; set; }

Note:
The name of the event callback can be named differently but in that case it is required to specify it when binding a value using
@bind-{Property}:event=”NameOfPropertyChanged”.

It means that the two notation are equivalent when binding a value:

<Child @bind-Year="Text" /><Child @bind-Year="Text" @bind-Year:event="TextChanged" />

Once the properties are defined, the component will have to invoke the event callback when the property value is changing.

await TextChanged.InvokeAsync(valueOfUserTextInputFromModalDialog);

Inject the appropriate services

To use the Java Script interop and the “ExampleJsInterop” example class we have to use an injected instance of “IJSRuntime”. We can do it with a dedicated attribute “Inject” in the component C# file.

 [Inject]
private IJSRuntime JsRuntime { get; set; }

Implement the behavior

Now we have all we need to implement the behavior by adding a button with a click handler that is going to call the JS function through the “ExampleJsInterop.Prompt” method. Once we got the result we just need to forward to the “TextChanged” event call back and to update our “Text” property value.

In the razor file:

<button
class
=”btn btn-primary”
@onclick=”GetTextAsync”>Click me</button>

And in the cs file:

private async Task GetTextAsync()
{
// Call JS interop.
var text = await ExampleJsInterop.Prompt(
JsRuntime,
“Enter some text:”,
Text);
// Trigger the changed event.
await TextChanged.InvokeAsync(text);
// Set the property value.
Text = text;
}

At the end it gives us the source for Component1.cs and Component1.razor :

Use your component

Case without custom resources

If the component is not using any custom resources (css, js or any other resources), the component integration can just be a Nuget package reference declaration in the application project.

<ItemGroup>
<PackageReference Include="MySharedComponents" Version="1.0.0" />
</ItemGroup>

Or a project reference if we are in the same solution:

<ItemGroup>
<ProjectReference Include=”..\MySharedComponents\MySharedComponents.csproj” />
</ItemGroup>

Case with custom resources

In our example, we need to do additional manual operations to be able to use the component.

More precisely, the component is using some custom resources in the “wwwroot” folder that need to be declared in the Java script environment.

In out example we need to add a link HTML element in the head part of the document to reference the “styles.css” and a script element in the body part to reference the “exampleJsInterop.js” like this:

<head>
<link href="_content/MySharedComponents/styles.css" rel="stylesheet" />
</head>
<body>
<script src="_content/MySharedComponents/exampleJsInterop.js"></script>
</body>

Depending on the hosting model it is required to do it in specific index files in the application project:

wwwroot/index.html for the Web Assembly hosting model.

and

Pages/_Host.cshtml for the Server-side hosting model.

Tips:

Make sure that your host is setup to serve Static Web Assets because the resources are going to be downloaded from the client browser. You may need to add “webBuilder.UseStaticWebAssets();” in your Program.cs.

--

--