Blazor common error component


Problem

Actually, on every page and in every component exception can be thrown and client should have ability to understand that’s something goes wrong. Unfortunately, there still is no global error handling mechanism in blazor to catch exceptions. So developers have to wrap all methods with try catch and show error information to user in every page and component. However, last part can be optimized and we can create single component which will show error information and can be accessed from any layer of components in our components tree.

Solution

Firstly, let’s create blazor webassembly application. Then create folder Components in the root of project. Finnaly, add new class with name IErrorComponet.

This components has following content :

public interface IErrorComponent
{
    void ShowError(string title, string message);
}

So as we can see, it just has one method for showing error message with title and information message;

Next step is modifying MainLayout as implementation of our IErrorComponent interface.

@using Components

@inherits LayoutComponentBase
@implements IErrorComponent

<div class="sidebar">
    <NavMenu />
</div>

<div class="main">
    <div class="top-row px-4">
        <a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a>
    </div>

    <div class="content px-4">
        @if (isErrorActive)
        {        
        <div class="alert alert-danger" role="alert">
            <button type="button" class="close" data-dismiss="alert" aria-label="Close" @onclick="HideError">
                <span aria-hidden="true">&times;</span>
            </button>
            <h3>@title</h3>
            <p>@message</p>
        </div>
        }



    <CascadingValue Value="this" Name="ErrorComponent">
        @Body
    </CascadingValue>

    </div>
</div>

@code {

    bool isErrorActive;
    string title;
    string message;

    public void ShowError(string title, string message)
    {
        this.isErrorActive = true;
        this.title = title;
        this.message = message;
        StateHasChanged();
    }

    private void HideError()
    {
        isErrorActive = false;
    }
}

Firstly, we added next line of code

@implements IErrorComponent

It says that our layout implement our IErrorComponent interface, so it should have ShowError method. Which you can see in @code section. We also added alert part :

@if (isErrorActive)
{        
<div class="alert alert-danger" role="alert">
    <button type="button" class="close" data-dismiss="alert" aria-label="Close" @onclick="HideError">
        <span aria-hidden="true">&times;</span>
    </button>
    <h3>@title</h3>
    <p>@message</p>
</div>
}

which will show alert message if isErrorActive field is true.

And last one, but the important one part is wrapping body with CascadingValue component

<CascadingValue Value="this" Name="ErrorComponent">
    @Body
</CascadingValue>

With cascading parameters we can pass the paramter to any of components in a subtree.

So for example, we can easily use our component on FetchData page. Let’s modify into next form :


@page "/fetchdata"
@inject HttpClient Http
@using Components

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from the server.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    private WeatherForecast[] forecasts;

    [CascadingParameter(Name = "ErrorComponent")]
    protected IErrorComponent ErrorComponent { get; set; }

    protected override async Task OnInitializedAsync()
    {
        try
        {
            forecasts = await Http.GetJsonAsync<WeatherForecast[]>("sample-data/notfound.json");
        }
        catch(Exception e)
        {
            ErrorComponent.ShowError(e.Message, e.StackTrace);
        }
    }

    public class WeatherForecast
    {
        public DateTime Date { get; set; }

        public int TemperatureC { get; set; }

        public string Summary { get; set; }

        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    }
}

Here we suppose that GetJsonAsync method can throw any exception, so if it does we catch it and show information to our user. NOTE: it is just example, don’t do it on production. As you can see, we get CascadingParameter as ErrorComponet so we can use it in our code and show error. This method call our layout and show common error alert.

Conclusion

Though, we don’t have ability to create global error handler yet, we can create one component for error rendering and use it anywhere in subtree of components.

You can found source code of that example here.

Buy Me A Coffee

Related Posts

Avoid reflections of mappers and let Mapster generate mappers for you

Mapster generating tool for onion application

Predict Bitcoin price with ML.net

Live time series coin price predictor with machine learning

Throw exceptions from backend to frontend with blazor

One of advantages of using same code language on both frontend and backend

How to avoid violating of SOLID principles while extending service behaviours

Step by step extending service behaviour with decorator pattern respecting SOLID

Blazor render optimization

Best practices

.Net 6 brings application state to blazor webassembly server prerender

It kills strange blinking on screen

Must have libraries for blazor

List of best nuget packages

Blazor virtualize component

Lazy loading of big lists. Rendering optimization.

Blazor grpc - comunication optimization

Smaller and faster requests to your backend from blazor wasm

Free database for your blazor app

Don't pay for the cloud