Using toast notifications in ASP.NET Core Razor Pages
Tags: webdev, dotnetcore, csharp, javascript
Published: 2023-06-19 19:35:32

The purpose of a toast allows users to know that their action was acknowledged or that something happened like an email has arrived in a polite manner.

> **Note**
> From Bootstrap: Toasts are lightweight notifications designed to mimic the push notifications that have been popularized by mobile and desktop operating systems.

## Code samples

There are plenty of code samples which interact between HTML, JavaScript and C# unlike other code samples on the web which generally exclude C#.

## Libraries

In this article several libraries will be presented with favor towards Bootstrap as Bootstrap supports Accessibility while the other libraries do not support Accessibility. Accessibility is very important as without making a web site accessible customers may be lost.

## Bootstrap

To get started, read the documentation for [toast](https://getbootstrap.com/docs/5.0/components/toasts/).

:heavy_check_mark: One of the major benefits for Bootstrap is its suitable for screen readers which is discussed in Bootstrap documentation while the other libraries are not capable of working with screen readers.

Typically one sees toast in the bottom right hand corner of a page and the same for Windows Desktop toast.

Example, create a container for a toast

```html
<div class="toast-container position-fixed bottom-0 end-0 p-3" style="z-index: 11">
```

position-fixed bottom-0 end-0 tells Bootstrap to position a toast in the bottom right hand corner of a page.

Add a toast to the above container

```html
<div class="toast text-white bg-danger border-0" 
     id="bottomToast" 
     role="alert" 
     aria-live="assertive" 
     aria-atomic="true">

    <div class="d-flex">
        <div class="toast-body">
            @Model.Bottom
        </div>
        <button type="button" 
                class="btn-close btn-close-white me-2 m-auto" 
                data-bs-dismiss="toast"
                aria-label="Close"></button>
    </div>
</div>
```

Model for setting the body text of the toast.

```csharp
public class MessageContainer
{
    public string Top { get; set; }
    public string Bottom { get; set; }
}

...

[BindProperty]
public MessageContainer MessageContainer { get; set; }
```

In OnGet the code is setup to receive text for two toast and if not pasted has defaults. In the code sample, on the Index Page there are two inputs which when submitting the form redirects to the Privacy page passing data via Json while going directly to the  Privacy page the default data is used.

```csharp
public void OnGet(string containers)
{
    MessageContainer = new MessageContainer();

    if (!string.IsNullOrWhiteSpace(containers))
    {
        List<ToastContainer>? data = JsonSerializer.Deserialize<List<ToastContainer>>(containers);

        MessageContainer.Top = data.FirstOrDefault(x => x.Name == "Top").Body;
        MessageContainer.Bottom = data.FirstOrDefault(x => x.Name == "Bottom").Body;
    }
    else
    {
        MessageContainer.Top = "Top message";
        MessageContainer.Bottom = "Bottom message";
    }
}
```

To display the toast, the following JavaScript is used.

```javascript
function bottomToast() {
    var toastElList = [].slice.call(document.querySelectorAll('#bottomToast'));
    var toastList = toastElList.map(function (toastEl) {
        return new bootstrap.Toast(toastEl);
    });
    toastList.forEach(toast => toast.show());
}
```
Results

![Displaying bottom toast](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b5niv80s5x88dw0l1z3l.png)

**Center toast on a page**

Although toast as generally displayed in the bottom right hand corner of a page, they can be positioned top or centered on a page.


![Displaying toast center page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ochzr0hclb313uminw2b.png)

For the above we use several Bootstrap classes and set the height of a div container.

```html
<div aria-live="polite" 
     aria-atomic="true" 
     class="d-flex justify-content-center align-items-center w-100" 
     style="height: calc(100vh - 125px - 125px);">

    <div class="toast bg-danger"
         role="alert"
         aria-live="assertive"
         aria-atomic="true"
         id="toast1">
        
        <div class="toast-header">

            <strong class="me-auto">Alert</strong>
            <small>code sample</small>

            <button type="button" 
                    class="btn-close" 
                    data-bs-dismiss="toast" 
                    aria-label="Close"></button>
        </div>

        <div class="toast-body text-white">
            This toast is centered on the page
        </div>

    </div>
</div>
```
Using JavaScript to display the toast above.

```javascript
$("#toastButton1").click(function () {
    $("#toast1").toast("show");
});
```
**Asking a question**

![toast asking yes no question](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9tjtqrbcv827fxjjtje.png)

Here we add two buttons to the toast body with identifiers.

```html
<div class="toast" 
     role="alert" 
     id="buttonExample"
     aria-live="assertive" 
     aria-atomic="true">
    <div class="toast-body">
        Click [Take action] button!
        <div class="mt-2 pt-2 border-top">

            <button type="button"
                    class="btn btn-primary btn-sm"
                    id="takeAction">
                Take action
            </button>

            <button type="button"
                    class="btn btn-secondary btn-sm"
                    id="noAction"
                    data-bs-dismiss="toast">
                Close
            </button>

        </div>
    </div>
</div>
```

Add a property to the page which will be set in JavaScript.

```csharp
[BindProperty]
public bool TakeAction { get; set; }
```

JavaScript to handle the two buttons click events.

```javascript
document.getElementById("takeAction").addEventListener("click", function () {
    document.getElementById("takeActionValue").value = true;
    document.getElementById("form1").submit();
});

document.getElementById("noAction").addEventListener("click", function () {
    document.getElementById("takeActionValue").value = false;
    document.getElementById("form1").submit();
});
```

Then OnPost, in this case using SeriLog display yes or no where YesNo is a language extension.

```csharp
public void OnPost()
{
    Log.Information("Take action {A}", TakeAction.ToYesNo());
}
```

## Using an alert
There is always the option to use an [dismissing Alert](https://getbootstrap.com/docs/5.1/components/alerts/#dismissing) which in the example below waits three seconds than displays.

```html
@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="container">
    <div class="alert alert-primary alert-dismissible fade show d-none"
         role="alert"
         id="customAlert">

        <strong>Attention</strong> time to take a break.
        <button type="button" class="btn-close" 
                data-bs-dismiss="alert" 
                aria-label="Close"></button>

    </div>
</div>

@section Scripts
{
    <script>
        document.addEventListener("DOMContentLoaded", handleClick);

        async function handleClick() {
            await new Promise((resolve) => setTimeout(resolve, 3000));
            document.querySelector("#customAlert").classList.remove("d-none");
        }
    </script>
}
```

## AspNetCoreHero.ToastNotification

[ToastNotification](https://github.com/aspnetcorehero/ToastNotification) is a Minimal & Elegant Toast Notification Package for ASP.NET Core Web Applications that can be invoked via C#. Compatilble with ASP.NET Core 3.1 and .NET 5. (works with .NET Core 6 and .NET Core 7).

- Not WCAG complaint 
- Not the best documentation

![AspNetCoreHero page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rdib3hiqea11rm0sr0ok.png)

Simple setup

```csharp
private readonly INotyfService _toastNotificationService;
public ToastPageModel(ILogger<ToastPageModel> logger, INotyfService toastNotificationService)
{
    _toastNotificationService = toastNotificationService;
}
```

Usage

Add a button

```html
<input type="submit" class="btn btn-success"  value="Success"
       asp-page-handler="SuccessNotification"/>
```

Add a Post event

```csharp
public void OnPostSuccessNotification()
{
    _toastNotificationService.Success("Work finished");
}
```

Customized version

```csharp
public void OnPostCustomNotification()
{
    _toastNotificationService.Custom(
        message: "Email sent", 
        durationInSeconds: 2, 
        backgroundColor: "black", 
        iconClassName: "fa fa-gear");
}
```
## Wrapping it up

The clear choice for robust toast that are WCAG compliant is Bootstrap. There are others that are paid for such as Kendo notifications which I have a sample [here](https://github.com/karenpayneoregon/web-app-code-samples/blob/master/TelerikSamples1/Pages/NotificationIndex.cshtml).

Everyone will have their own choice which is why in the provided source code there are several other libraries that can be tried out.

## Requires 

- Microsoft Visual Studio 2022 or higher
- .NET Core 7 (with minor changes like scoped namespaces will worjk with .NET Core 6)

## Source code

Clone the following GitHub [repository](https://github.com/karenpayneoregon/toast-notifications).

There is a lot more code than presented in this article.

### Caveats

In several pages CSS and JavaScript has been nested. The JavaScript is kind of bleeding edge as sometimes the code gets cached and/or Visual Studio complains about overwriting a file.


![nested files](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n3gks1avlceinh99qdzc.png)