Using LanguageExt with Blazor to make a better busy indicator

I previously blogged about creating a busy indicator in Blazor. That works fine, but there is more we can do.

As my loyal reader knows, I have been trying to make my code more functional for some time, and have been using the rather fabulous LanguageExt nuget package to help.

One of the most basic, but extremely useful parts of this package is the `Option` monad (oh no, the m-word!). This allows you to handle the case where the data you want doesn’t exist, without having to allow those naughty null references creep in your code. I was wondering how we could handle this in Blazor.

GitHub user orthoxerox suggested creating a Blazor component with two render fragments, one for the `Some` case and one for the `None` case. This was simple, but very effective.

Whilst playing with this, it occurred to me that we could extend it to combine the busy indicator as well. This would allow us to show a “Loading…” message while the data was being loaded, and then either the `Some` or `None` content, depending on what was returned. The code is pretty simple, and is an obvious combination of the two components…

@using LanguageExt
@typeparam T

@if (!Data.HasValue) {
  <p><span class="fad fa-spin fa-spinner" aria-hidden="true"></span> @Message</p>
} else {
  @Data.Value.Match(x => Some(x), None);
}

@code {

  [Parameter]
  public string Message { get; set; } = "Loading...";

  [Parameter]
  public Option<T>? Data { get; set; }

  [Parameter]
  public RenderFragment<T> Some { get; set; }

  [Parameter]
  public RenderFragment None { get; set; }

}

Assuming you have this saved in a file called `BusyOption`, which isn’t a very imaginative name, but does describe what it does, then you can use it like this…

@page "/BusyOptionTest"
@using LanguageExt

<h3>Busy option</h3>

<div>
  <BusyOption Data="_customer" Context="customer">
    <Some>@customer.Name is @customer.Age years old</Some>
    <None>No customer</None>
  </BusyOption>
</div>

<button @onclick="SetCustomer">Set to Bob</button> <button @onclick="SetNoCustomer">Set to unknown</button>

@code {
  public Option<Customer>? _customer;
}

@functions {
  private void SetCustomer() =>
    _customer = Get(1, "Bob", 42);

  private void SetNoCustomer() =>
    _customer = Option<Customer>.None;

  public record Customer(int Id, string Name, int Age);

  private static Option<Customer> Get(int Id, string Name, int Age) =>
    new Customer(Id, Name, Age);
}

This is obviously a very simplistic sample, but it shows the idea. When you first load the page, you see the “Loading…” message, and then when you click one of the buttons, you see what would happen if the customer were found (`Some`) or not (`None`).

Another example of how Blazor can make life sooo much easier.

This got me thinking – no mean feat in itself! There are some obvious ways you could take this further. it wouldn’t be hard to write a ValidationBlazor component that took a Validation object and displayed the error messages (if there were any). You could write an EitherBlazor component to handle the two cases that Either produces. The possibilities are (almost) endless.

2 Comments

  1. Ricardo Valero De La Rosa said:

    Hi, I came across your issue on LanguageExt. I think you are up to something great. I was also thinking about validation, but maybe then state management like Fluxor but with LanguageExt read, write and state monads? Keep this up!

    February 22, 2021
    Reply
  2. Avrohom Yisroel Silver said:

    @Ricardo I looked at Fluxor, but decided it was adding too much complexity for my needs. Might revisit it some day though.

    As for LanguageExt monads, I think that’s the way to go, but I haven’t got my head round the FP stuff enough to be there yet.

    April 11, 2021
    Reply

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.