topic: ASP .NET by Milos Protic relates to: Web Development, Blazor, Components, Introduction, WebAssembly, Wasm on November, 15 2019
Introduction to Blazor Components and Their Usage
Introduction
In my previous article related to Blazor, I wrote about the state of SPA (Single Page Application) development and some exciting new possibilities available to us as web developers. That was my first time using Blazor, and I must say that I liked it. I think it has a bright future.
A fraction from the previous post:
Blazor is a SPA (Single-Page-Application) framework created by Microsoft. It's component-based and enables developers to build a rich client-side UI with .NET running in the background. This means that we can write C# instead of JavaScript and get the same result. This is possible thanks to the WebAssembly. For many people, this is a big "Yay" or "Finally", considering that not everyone loves JavaScript. They are simply forced to use it because of the monopoly it has.
Full article: Blazor - Getting Started and First Impressions
In this article, I will go deeper and focus on components. After all, they represent the main building block in our application.
Before diving in, I should mention that, at the moment of writing this, Blazor Server was officially supported in ASP.NET Core 3.0, and Blazor WebAssembly was in preview for ASP.NET Core 3.1
What is a Component?
A component represents, as mentioned above, the main building block in our application. It's a standard, accepted across many frameworks, used to build an application part by part, or component by component. Ideally, they are decoupled and self-contained, and each of them has it's own logic and styling. That makes them easily reusable and our application more maintainable. If we build our application with components, we can easily pinpoint the place where an update or fix is required.
What is a Blazor Component?
A component in Blazor framework (referred to as Razor component) is no different, and the rules above are applied to them as well. It is implemented within a file with a .razor extension using a combination of HTML markup with C#. HTML markup is used to define the UI and by using C# we write the logic behind the component.
All component related logic must be placed within the @code
block, meaning that we define its state (properties, methods) within it. Also, more than one @code
block is allowed to have.
An ExampleComponent.razor file looks like this:
template:
<section>Hi from Example Component</section>
logic:
@code {
// ... members
}
A razor component state is managed with fields and parameters. Fields represent something like a local state, or variables. They are private and cannot be provided by the parent component. To render these values within the template, we add a C# expression starting with the prefix @
. For example:
<section>
@PrivateMessage
</section>
code block:
@code {
private string PrivateMessage = "Hello!";
}
Component parameters will be described in the next section.
A component's name must start with an uppercase character (ExampleComponent.razor) and we can use it in another component:
<ExampleComponent />
or within a razor page:
<section id="ExampleComponent">
@(await Html.RenderComponentAsync<ExampleComponent>(RenderMode.ServerPrerendered))
</section>
The second way is particularly useful in situations when we have a legacy MVC project and we want to integrate Blazor components with it. The kind people from Microsoft have given us this possibility and I find it very useful.
Also, many front-end frameworks limit the component to have a single root tag in its template. This limitation doesn't exist in Blazor framework, meaning that we can define a component with multiple root tags.
Parameters
Each component, besides fields, can have parameters. We can define as many as we like, and create one by simply placing an attribute on the public field. These fields represent the component arguments available to be passed down within the parent's HTML markup.
It's similar to Vue's way of passing down values through data and props.
A simple parameter looks like this:
@code {
private string PrivateMessage = "Hello!";
[Parameter]
public string Message { get; set; }
}
and we pass it from the template by setting up the attribute within the markup:
<ExampleComponent Message="Hi!" />
The Special ChildContent
Parameter
This is a reserved parameter that represents the content passed/provided by the parent. The type of this property should be RenderFragment
. The parent component can pass this parameter value by placing the content between the child component open and closing tags.
Child component HTML:
<section>
@PrivateMessage
@Message
@ChildContent
</section>
code:
@code {
private string PrivateMessage = "Hello!";
[Parameter]
public string Message { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
}
and the parent HTML:
<ExampleComponent Message="Hi!">
Child Content Goes Here...
</ExampleComponent>
This special parameter must be named ChildContent by convention
Arbitrary Parameters
This feature is giving us the ability not to create a parameter for each attribute for our template. Let's say that we have an input which requires attributes required
and maxlength
to be set. Now, we could create a parameter for each attribute and set it in our template, but this is a longer, more verbose way of doing it. Instead, we can create a single parameter (note that it must be a dictionary) with the CaptureUnmatchedValues
property set to true
.
This parameter will match all attributes that don't match any of the other parameters defined in our component. The following example illustrates this parameter in action.
template:
<section>
@PrivateMessage
@Message
@ChildContent
<input @attributes="Attributes" />
</section>
code:
@code {
private string PrivateMessage = "Hello!";
[Parameter]
public string Message { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object> Attributes { get; set; }
}
usage:
<ExampleComponent Message="Hi!" required="true" maxlength="10">
Child Content Goes Here...
</ExampleComponent>
Data Binding
Data binding gives us the ability to bind, or connect if you like, our UI with the underlying data model, in our case the members from the @code
section. In the Blazor framework, this is accomplished by using the @bind
keyword in our templates.
Let's update our component to implement data binding. Also, we will add a span to show the updated value.
template:
<section>
@PrivateMessage
@Message
@ChildContent
<input @attributes="Attributes" @bind="Name" />
<span>@Name</span>
</section>
code:
@code {
// ... removed for clarity
public string Name { get; set; } = "John";
}
The code above is all we need to implement the two-way data binding, meaning that whenever we change the Name
property the input value will be updated, and vice versa. But, there is a catch. By default, after changing the input value, the Name
property will be updated when the input loses focus.
When it comes to data binding, we as developers have grown attached to seeing an immediate UI update after the value is changed. Luckily, this is supported in the Blazor framework as well, we just need to tweak our template a little bit. To accomplish the desired behavior, our binding should look like this:
<input @attributes="Attributes" @bind-value="Name" @bind-value:event="oninput" />
What we did here is that we've updated our @bind
attribute by telling it exactly which input attribute to bind and when to update the corresponding value. By doing this, we achieved the instant UI update as we are typing.
Interaction With the Parent
So far, our bound field values were hardcoded and static. This is not ideal and in practice, we often have situations when we need to bind a child component parameter to a parent's component parameter of field, or to update a parent field from one or more child components. You will agree with me that this is not that uncommon request.
With Blazor, this is possible with the @bind
attribute. It recognizes the component parameters, meaning that this is valid syntax: @bind-{property}
. We will use this approach to bind our child parameter to the parent field. On the other hand, updating the parent field value is done with event callbacks.
Let's update our component and bind our message parameter to the parent field.
Child template remains the same:
<section>
@PrivateMessage
@Message
@ChildContent
<input @attributes="Attributes" @bind="Name" />
<span>@Name</span>
<button @onclick="UpdateMessage">Update Message</button>
</section>
and in the code section, we need to add an event callback:
@code {
// ... removed for clarity
[Parameter]
public EventCallback<MouseEventArgs> UpdateMessage { get; set; }
}
Now, in our parent component, we will define the message and event responsible for updating it. In the template, we will use @bind-Message
attribute to bind our child message to the parent field and the event UpdateMessage
will be passed down like any other parameter.
<ExampleComponent @bind-Message="Message" required="true" maxlength="5" UpdateMessage="UpdateMessage">
Child Content Goes Here...
</ExampleComponent>
Code block:
@code {
public string Message { get; set; } = "Hello";
private void UpdateMessage(MouseEventArgs e)
{
Message = "Hello from child";
}
}
Now, after clicking the button in the child component, the default Hello
text should change to Hello from child
.
Events
In the previous section, we used events without explaining them. Don't worry, it's not complicated, but it deserves to be properly introduced.
Each razor component can have its own events. They are attached to an element by using an HTML attribute with a prefix on
, for example, onchange
. Every attribute with this prefix tells the framework that the value should be treated as an event. To create an event, we simply define a method in the components @code
block.
The template:
<button @onclick="SayHi">
Say Hi
</button>
The code block:
@code {
private void SayHi(MouseEventArgs e)
{
Console.WriteLine("Hi");
}
}
Extra Parameters
This is all nice and neat, but what if we need to pass an extra parameter to our method? We cannot simply assign the event as an attribute value. Here is where lambda expressions come into place. Let's update our component.
The template:
<button @onclick="@(e => SayHi(e, "Hi"))">
Say Hi
</button>
The code block:
@code {
private void SayHi(MouseEventArgs e, string message)
{
Console.WriteLine(message);
}
}
After clicking on the button, we should see Hi
in the browser console in both cases!
I think that you should be familiar with the component basics at this point and I will conclude this article with the events section. Don't be mistaken, Blazor has a lot more to offer and I will share my impressions about routing, inheritance and other useful features in one of the next articles.
Conclusion
The components made our lives easier, our apps more maintainable and our codebase nicely organized. In my opinion, they are one of the best things that happened to web development since JQuery was released. We should all embrace the power and simplicity of the components and stand proud behind our scalable apps built by using them.
You are welcomed to subscribe here or follow me on twitter to stay tuned. Feel free to express your thoughts in the comment section below. If you wish to support me and see more articles like this, consider buying me a coffee.
Thank you for reading and see you in the next article!
Subscribe to get the latest posts delivered right to your inbox