Building an alert service in Blazor (Client-side)
Tags: dotnet, blazor, tutorial
Published: 2020-02-01 15:28:22

I've been mostly playing with AngularJS and Angular for the last few years at Packt as a dev manager, but now I'm moving to a new role where they've chosen to build a platform with [Blazor](https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor).

I'm totally brand new to Blazor, and haven't used .Net since 4.0 came out (original framework, Core didn't exist then). I'm going to write up some of my discoveries as part of this migration to be used as a reference for anyone else starting out.

#Why an Alert Service?

All the tutorials I've seen so far are fairly static, with minimal interaction between pages and components (hint, pages are components). A UI often needs a way of letting a user know something has happened. It may be directly related to the component they are interacting with, in which case popping up a message is pretty easy, but doing it for every component can mean being repetitive. A background service may need to let a user know something has happened, or a component that is going out of focus. In this case, as a component out of focus can't display anything, nor can a service, we need something else to manage messages.

My requirements were pretty simple - ability to show at any time, in a range of colours (following Bootstrap) and automatically disappear after some seconds.

I originally looked at a component in the MainLayout that was accessible down the stack through the use of `CascadingValue` but it got messy around the redraw and having events for making messages disappear.

#The code structure

```
= Shared
  = Interfaces
      IAlert.cs
  = Classes
      Alert.cs
  = Enums
      Alerts.cs
  = Services
      AlertService.cs
  MainLayout.razor
= Components
    AlertComponent.razor
= wwwroot
  = css
      alert-service.css
  index.html
Program.cs
```

#The Code Files

We have an `interface` to define the type for the alerts being passed around, not absolutely necessary but useful for mocking tests later on:

```csharp
public interface IAlert {
    string message { get; set; }
    Alerts alert { get; set; }
}
```

---

A `class` is then used to actually hold the data. Coming from TypeScript, this is unnecessary as anonymous classes do the job perfectly well.

```csharp
public class Alert : IAlert
{
    public Alert() {}
    public Alert(string message, Alerts alert) {
        this.message = message;
        this.alert = alert;
    }

    public string message { get; set; }
    public Alerts alert { get; set; }
}
```

---

The `enum` is used to hold all the different types of alert: `Primary`, `Secondary`, `Danger`, `Warning`, `Info` etc.

```csharp
public enum Alerts {
    Primary,
    Secondary,
    Success,
    Info,
    Danger,
    Warning,
    Dark,
    Light
}
```

---

The `AlertService` defines our actual code:

```csharp
using System;
using System.Collections.Generic;

public class AlertService {
    public List<IAlert> messages { get; private set; }
    public event Action RefreshRequested;

    public AlertService() {
        this.messages = new List<IAlert>();
    }

    public void addMessage(Alert alert) {
        this.messages.Add(alert);
        System.Console.WriteLine("Message count: {0}", this.messages.Count);
        RefreshRequested?.Invoke();

        // pop message off after a delay
        new System.Threading.Timer((_) => {
		    this.messages.RemoveAt(0);
		    RefreshRequested?.Invoke();
	    }, null, 8000, System.Threading.Timeout.Infinite);
    }
}
```
There is a private property to hold the messages in a list, and a public method to add to the list.

There is also an event setup for when data changes. Then anything that wants to use the data from the service can be notified when changes occur. This event is referenced twice in the`addMessage` method - once when the alert is added to the list, and once when it is removed.

You can see we have a timer set to 8 seconds to remove the top item in the list. If for some reason the list is empty, this could potentially cause problems - ideally some defences should be added here.

---

The `AlertComponent` has the physical layout and rendering method:

```razor
@inject AlertService AlertService

<div class="alert-service">
    @for (int c = 0 ; c < AlertService.messages.Count ; c++)
    {
        <span class="alert @getAlertClass(AlertService.messages[c])">@AlertService.messages[c].message</span>
    }
</div>

@code {
    protected override void OnInitialized() {
        Console.WriteLine("AlertComponent:Initializied");
        AlertService.RefreshRequested += Refresh;
    }

    private String getAlertClass(IAlert alert) {
        return String.Format("alert-{0}", alert.alert.ToString().ToLower());
    }

    private void Refresh() {
        Console.WriteLine("AlertComponent:refreshing");
        StateHasChanged();
    }
}
```

Firstly, we inject the `AlertService` into the component. There are changes in the `Program.cs` below that make this possible.

We then have some HTML with a razor iterator to build the messages. This is slightly inefficient as every update will recreate all the DOM elements, rather than adding and removing as necessary. This would be much worse if we had a more complex structure, but as it is, it isn't completely terrible. I did try using a [BlazorStrap  Alert](https://blazorstrap.io/alerts) but it had issues with reading data out of sync (no idea why yet).

The code is mostly just helpers. When the component is initialised, the AlertService `RefreshRequested` event is subscribed and wired to the `Refresh()` method.

The `getAlertClass()` method performs the required manipulation to build the CSS class from the Alert object containing the enum.

---

We put our component into `MainLayout` with simply:

```html
<AlertComponent />
```

In `alert-service.css` we have some fairly simple CSS:

```css
.alert-service {
    position: absolute;
    top: 4rem;
    right: 2rem;
}

.alert-service span {
    display: block;
    width: 20rem;
    line-height: 2.2rem;
    text-align: right;
    padding: 0.8rem;
    margin: 0.4em;
}


.alert {
    color: #fff;
    border-style: solid;
    border-width: 0 1px 4px 1px
}

.alert-primary {
    background-color: #158CBA;
    border-color: #127ba3
}

...snip...
```

Finally, in `Program.cs` we have to register our service to make it available to components. We add the following line next to where the App is registered (shown together):

```csharp
builder.RootComponents.Add<App>("app");
builder.Services.AddSingleton<AlertService>();
```

Now, we can use the `AlertService` in any component.

For example, I added it to the default `Counter` page/component:

```razor
@page "/counter"
@inject AlertService AlertService
<PageTitle Title="Counter" @ref="PageTitle" />

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private PageTitle PageTitle;
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
        this.PageTitle.Title = String.Format("Counter: {0}", currentCount);
        Console.WriteLine("Incrementing");
        AlertService.addMessage(new Alert(currentCount.ToString(), Alerts.Info));
    }
}
```

You can see we inject the AlertService at the top, as in the AlertComponent. We can then reference it in the `IncrementCount()` method based on the definition in the injection. We add our message, and it shows on screen for 8 secs.

Future changes for this setup would allow for persistent, manually dismissable messages, and to ensure HTML safety in the messages displayed to ensure no XSS capability.

[PageTitle](https://dev.to/supachris28/changing-the-page-title-in-blazor-client-side-880) is another problem I solved....