DotNetT Light_870x220

In this article we'll look into when it's helpful to use a code-behind in your Blazor development, how we benefit and what to expect when re-factoring existing components.

When working with Blazor the default component architecture is to have all of the markup and logic mixed within a single file (.razor). When components are simple this approach works well, however as the complexity becomes greater managing the entirety of a component as a whole becomes difficult. Using a "code-behind" approach allows for markup and logic to be separated into their own files. In this article we'll look into when it's helpful to use a code-behind, how we benefit and what to expect when re-factoring existing components.

Single File Approach

Let's begin with a quick review of the single file approach. We'll use ASP.NET's default template's FetchData component as an example. The FetchData component displays an HTML table of weather forecast data by looping over data provided by a service. The component uses directives, HTML/Razor, and a @code block; these are all typical items that make up a component.

component-anatomy

The markup for the component is quite verbose. Weighing in at 30+ lines the markup itself extends the length of the file, causing us to scroll down to find the components logic represented by a @code block.

scroll-needed

Keep in mind that this component is still actually quite simple. As the complexity becomes greater each section will grow. This means we will eventually have more directives, using statements, markup, and logic in our code block. While it may be convenient to have an all inclusive file, it will eventually become tedious to maintain due to constant scrolling between between markup and logic.

Let's re-factor the FetchData component by using a code-behind file see how it improves the overall developer experience.

Code-Behind Approach

Code-behind is a common term for the technique of using a separate code file to that represents all of the logic for a corresponding page, view, or component. Creating a code-behind in Blazor requires a few steps, but thankfully it's supported by the framework so setup is quite simple. To complete our code-behind we'll need to create a class and then link the code-behind to our markup. Once we have the structure in place, we can move over our existing logic.

Creating and Linking a Code-Behind

First we'll need a class file that will represent our code-behind. This is a standard .NET class, however naming is very important. When we create our code-behind file we need to consider both the file name and class name as they can greatly effect our developer experience.

Let's start with the file name. When we add the file name we should follow a common convention of [componentName]Base.razor.cs. The .razor.cs extension is understood by Visual Studio and will be properly nested within the file explorer window. Since the component already occupies a class name we'll need to add the suffix Base to our class name giving it a unique class name. The final code-behind file should be FetchData.razor.cs with a class name of FetchDataBase.

file-nesting

Next, we'll need to link the markup portion of the component (.razor) to the code-behind (.razor.cs). In the code-behind file, we'll inherit from ComponentBase. When inheriting from ComponentBase, in Visual Studio the shortcut ctrl+. will add the using statement using Microsoft.AspNetCore.Components; which is the namespace for ComponentBase. At the time of writing the ctrl+. shortcut is unavailable in Razor as are many re-factoring tools for Visual Studio. Access to these shortcuts emphasizes the importance of code-behind files in Blazor.

using Microsoft.AspNetCore.Components;

namespace MyApp.Pages
{
    public class FetchDataBase : ComponentBase
    {
    }
}

We'll use a similar process in our markup file to complete the link between both files. At the top of the component's markup we'll inherit from our code-behind using the @inherits directive. Inheriting from FetchDataBase forms a complete component and we no longer need to use the @code block to define our component's logic.

@inherits FetchDataBase
@page "/fetchdata"
@using WebApplication33.Data
@inject WeatherForecastService ForecastService

...markup

@code { ... }

We can now migrate all of the code from the @code block to our code-behind, and a few directives as well.

Migrating @code to Code-Behind

Most of the logic from our @code block can be copied directly as-is to the code-behind without change, however there are some small updates that will need to be made. The @code block is scoped to the component's class, while the code-behind's scope is the component's base class. Anything we would like to reference in the base class via markup needs to be marked protected. In the case of FetchData, the variable forecasts is used in the components markup so it must be protected or public to be visible to the FetchData class. In the code-behind WeatherForecast[] forecasts; becomes protected WeatherForecast[] forecasts;.

    public class FetchDataBase : ComponentBase
    {
        protected WeatherForecast[] forecasts;

        ...
    }

As we move code to our code-behind, additional using statements will be required as the code-behind doesn't take advantage of global values in _Imports.razor. Once again this is where standard tooling comes in handy with ctrl+. making short work of any missing namespaces.

ctrl+period

using Microsoft.AspNetCore.Components;
using System.Threading.Tasks; // Added by ctrl + .
using MyApp.Data; // Added by ctrl + .

namespace MyApp.Pages
{
    public class FetchDataBase : ComponentBase
    {
        protected WeatherForecast[] forecasts;

        protected override async Task OnInitAsync()
        {
            forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
        }
    }
}

With all of the code migrated, we still need to tackle one final step, dependency injection (DI).

Dependency Injection in Blazor Code-Behind

Dependency Injection in Razor markup is handled by the @inject directive. While the directive is available in code-behind classes, it is written much differently and at first glance may not be obvious. To use dependency injection in a code-behind file we'll use the [Inject] attribute and apply it to a property of the type we would like to inject, ex: [Inject] MyType MyProp { get; set; }.

using Microsoft.AspNetCore.Components;
using System;
using System.Threading.Tasks;
using MyApp.Data;

namespace MyApp.Pages
{
    public class FetchDataBase : ComponentBase
    {
        [Inject] WeatherForecastService ForecastService { get; set; }

        protected WeatherForecast[] forecasts;      

        protected override async Task OnInitAsync()
        {
            forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
        }
    }
}

With the [Inject] attribute added the component can now resolve the service and we no longer need the directive in the Razor markup. We can proceed with cleaning up our markup by removing the @inject and @using directives as well as the @code block.

Code-Behind Final Thoughts

When we started the component's directives, markup and logic were all contained in a single file. While a single file approach provides benefits in its simplicity, and is great for small components, it simply doesn't scale well. Once our component requires complex markup or logic it's easy to get lost in the large file and/or confused by two different ways of expressing code. In addition, we lose access to some of Visual Studio's most valuable productivity tooling.

The process of converting an existing component to use a code-behind is quite easy. A few code changes are required with regards to using statements, protection levels, and dependency injection. For the most part Visual Studio even guides us through making the proper corrections to achieve a successful compilation.

side-by-side

When the transition is complete we benefit from clearly defined roles for markup and logic with a lot less scrolling. If we need to see the component in its entirety we can now snap the two windows side-by-side and focus on the task at hand.

New to Blazor?

If you're new to Blazor and want to start learning quickly, see the free Blazor Quick Start video series. In this instructional video series we learn about ASP.NET Core Blazor. Learn the basics of working with Blazor concepts like: components, events, data binding, and more. The course is guided by me, Ed Charbeneau, an industry pro and four-time Microsoft MVP.


Ed Charbeneau is a Developer Advocate for Telerik
About the Author

Ed Charbeneau

Ed Charbeneau is a web enthusiast, speaker, writer, design admirer, and Developer Advocate for Telerik. He has designed and developed web based applications for business, manufacturing, systems integration as well as customer facing websites. Ed enjoys geeking out to cool new tech, brainstorming about future technology, and admiring great design. Ed's latest projects can be found on GitHub.

Related Posts

Comments

Comments are disabled in preview mode.