Image for post
Image for post

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
Oct 28 · 5 min read

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

Create the component project

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

Image for post
Image for post

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
}
}

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>
Image for post
Image for post

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.

Image for post
Image for post

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>

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);

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; }

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

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>

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.

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.

YounitedTech

Le blog Tech de Younited, où l’on parle de développement…

Thanks to Slim Ayache, Thomas Goldstein, and Cédric Valiente

Xavier Solau

Written by

YounitedTech

Le blog Tech de Younited, où l’on parle de développement, d’architecture, de microservices, de cloud, de data… Et de comment on s’organise pour faire tout ça. Ah, et on recrute aussi, on vous a dit ?

Xavier Solau

Written by

YounitedTech

Le blog Tech de Younited, où l’on parle de développement, d’architecture, de microservices, de cloud, de data… Et de comment on s’organise pour faire tout ça. Ah, et on recrute aussi, on vous a dit ?

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store