How to Create Reusable Confirmation Modals in Blazor Server

Introduction

Confirmation modals are like pop-up messages that make using websites easier. In this article, we'll show you how to make these pop-ups in Blazor Server. We'll use special tools called parameters and EventCallback to make your code simpler, make it easier for users to interact with your site, and help your Blazor Server apps work better.

In Blazor, components are like the superheroes of web development, working together to create awesome websites. But for them to be a dream team, they need to talk to each other. That's where EventCallback and Parameter come in. In this article, we're going to learn how to create a reusable modal popup with the superpowers of these two concepts and see how they help components chat and work together smoothly in the world of Blazor.

What is a Parameter?

Parameters are values that are passed from a parent component to a child component. They allow you to communicate data downward in the component hierarchy. Parameters are unidirectional, meaning data flows from the parent to child components.

What is EventCallback?

Event callbacks are used to handle events in a child component and notify the parent component about the occurrence of an event. Event callbacks are unidirectional as well, but they allow the child component to send messages or notify the parent component about some user interaction or state change.

Create a reusable confirmation modal pop-up

Follow these steps to create a reusable modal pop-up.

Step 1. Create a Blazor Server project.

First, we need to create a Blazor Server project then you'll have a default page named index.razor. If you wish to customize this default page, you can do so by adjusting the route. For instance, you can change the route by adding @page "/your-custom-route" at the top of your desired page, allowing you to create your unique default page.

Step 2. Create a Model class named UserDetailsModel.cs.

Create a class named UserDetailsModel.cs and write this code.

namespace BlazorDemo.Data
{
    public class UserDetailsModel
    {
        public Int64 UserId { get; set; }
        public string  FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public int Age { get; set; }        
    } 
}

Step 3. Add an interface class.

Then add an interface named IUserDetails.cs and write this code to call a method to catch the data from the database.

using BlazorDemo.Data;
namespace BlazorDemo.Interfaces
{
    public interface IUserDetails
    {
        public Task<List<UserDetailsModel>>GetUserList();     
    }
}

Step 4. Add a class under the DAL folder.

Create a class named UserDetails.cs under the DAL folder and write this code to catch the data from the database.

using BlazorDemo.Interfaces;
using Dapper;
using System.Data;
namespace BlazorDemo.Data
{
    public class UserDetails : IUserDetails
    {
        private readonly DapperContext _context;
        private readonly ILogRepo _logRepo;
        public UserDetails(DapperContext dapperContext)
        {
            _context = dapperContext;
            _logRepo = new LogRepo(dapperContext);
        }
        public async Task<List<UserDetailsModel>> GetUserList()
        {

            List<UserDetailsModel> llst = new List<UserDetailsModel>();
            try
            {
                using (var connection = _context.CreateConnection())
                {
                    var procedure = "[Project_GetUsersDetails]";
                    llst = (await connection.QueryAsync<UserDetailsModel>(procedure, commandType: CommandType.StoredProcedure)).ToList();

                }
            }
            catch (Exception ex)
            {
               
            }
            return llst;
        }
    }
}

Code Explanation

using BlazorDemo.Interfaces;

Import statement for the IUserDetails interface, indicating that this class implements methods defined in that interface.

using Dapper;

Import statement for the Dapper micro-ORM, a lightweight and efficient object mapper used for database operations.

using System.Data

Import statement for the System.Data namespace, which provides ADO.NET types for working with data.

public class UserDetails : IUserDetails

Declaration of the UserDetails class, implementing the IUserDetails interface.

_context 

A private field of type DapperContext represents the Dapper database context. It's injected through the constructor.

_logRepo 

A private field of type ILogRepo, representing a repository for logging. It's initialized with an instance of LogRepo in the constructor.

public UserDetails(DapperContext dapperContext) 

Constructor that takes a DapperContext as a parameter, injecting the Dapper context into the class.

public async Task<List<UserDetailsModel>> GetUserList()

Implementation of the GetUserList method from the IUserDetails interface. This method retrieves a list of UserDetailsModel objects from the database.

using (var connection = _context.CreateConnection())

Creates a database connection using the _context Dapper context.

var procedure = "[Project_GetUsersDetails]"; 

Defines a stored procedure named "Project_GetUsersDetails" to be executed.

llst = (await connection.QueryAsync<UserDetailsModel>(procedure, commandType: CommandType.StoredProcedure)).ToList();

Executes the stored procedure asynchronously and maps the results to a list of UserDetailsModel objects.

catch (Exception ex) { /* Error handling */ }

Catches and handles exceptions that may occur during the database operation. In this case, the error is not handled beyond catching the exception.

return llst; 

Returns the list of UserDetailsModel objects obtained from the database.

Step 5. Add a razor component and it's code-behind class.

Now, create a Razor component named UserInfo.razor and its corresponding code-behind class named UserInfoBase.cs. In these files, we will implement a grid to show the data from the database. write this code in the UserInfo.razor page.

@page "/"
@inherits UserInfoBase;
@using BlazorDemo.Data

<RadzenGrid Data="@userDetails" TItem="UserDetailsModel" AllowPaging="true">
    <Columns>
        <RadzenGridColumn TItem="UserDetailsModel" Property="UserId" Title="User ID" Width="50px" />
        <RadzenGridColumn TItem="UserDetailsModel" Property="FirstName" Title="First Name" Width="100px" />
        <RadzenGridColumn TItem="UserDetailsModel" Property="LastName" Title="Last Name" Width="100px" />
        <RadzenGridColumn TItem="UserDetailsModel" Property="Email" Title="Email" Width="100px" />
        <RadzenGridColumn TItem="UserDetailsModel" Property="Age" Title="Age" Width="50px" />    
    </Columns>
</RadzenGrid>
}}

Code Explanation

@page "/" 

Specifies that this page is the default page for the application and can be accessed at the root URL.

@inherits UserInfoBase

Instructs the page to inherit from the UserInfoBase class, which typically contains the page's logic and event handling.

@using BlazorDemo.Data

Imports the BlazorDemo.Data namespace allows the usage of types defined in this namespace within the page.

<RadzenGrid> 

Blazor component that represents a data grid. It is bound to a collection of UserDetailsModel objects (userDetails). The AllowPaging="true" attribute enables pagination.

<Columns>

Defines the columns to be displayed in the grid.

<RadzenGridColumn> 

Represents a column in the grid. Each column is bound to a property of the UserDetailsModel class and has specific configurations like title, width, and other display settings.

write this code in the it's code behind page UserInfoBase.cs.

using BlazorDemo.Data;
using BlazorDemo.Interfaces;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;

namespace BlazorDemo.Pages
{
    public class UserInfoBase:ComponentBase
    {       
        public List<UserDetailsModel> userDetails = new List<UserDetailsModel>();
        protected bool loading = false;
        [Inject] IUserDetails iuserDetails { get; set; }
  
        protected override async void OnInitialized()
        {
            await GetStaffDoc();
            StateHasChanged();
        }

        public async Task GetStaffDoc()
        {
            loading = true;
            try
            {                         
                userDetails = await iuserDetails.GetUserList();               
            }
            catch (JSDisconnectedException ex)
            {
                // Ignore
            }
            catch (Exception ex)
            {
               
            }
            finally
            {
                StateHasChanged();
                loading = false;
            }
        }
    }
}

Now, when you run the project you can see this type of output.

Output

Blazor Server

Step 6.  Add one more column in the RadzenGrid.

Now, add a column on this grid and show a delete button to delete the record so add this code in the UserInfo.razor page.

 <RadzenGridColumn TItem="UserDetailsModel" Context="user" Filterable="false" Sortable="false" TextAlign="TextAlign.Center" Width="50px" Title="Action">
            <Template Context="user">
                <RadzenButton ButtonStyle="ButtonStyle.Danger" Icon="delete" Class="m-1" Click=@(args => DeleteRow(user)) @onclick:stopPropagation="true"></RadzenButton>
            </Template>
</RadzenGridColumn>

Then I will show a confirmation modal popup on this delete button click, so add a razor component named Confirmation.razor and write this code.

@if (Show)
{
    <div class="model show" tabindex="-1" role="dialog" style="display:block; position: absolute; top: 0; left: 0; right: 0; margin: auto; z-index: 100;">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <div class="model-title">
                        <h3>@Title</h3>
                    </div>
                    <button type="button" class="btn-close" data-dismiss="modal" aria-label="Close" @onclick="() => Confirm(false)">
                    </button>
                </div>
                <div class="modal-body">@massage</div>
                <div class="modal-footer">
                    <button class="btn btn-danger" @onclick="() => Confirm(true)">Yes</button>
                    <button class="btn btn-dark" @onclick="() => Confirm(false)">No</button>
                </div>
            </div>
        </div>
    </div> 
}
@code {
    public bool Show{ get; set; }
    [Parameter]
    public string Title { get; set; } = "Delete Confirmation";
    [Parameter]
    public string massage { get; set; } = "Are you sure?";
    [Parameter]
    public EventCallback<bool> OnResponseClick { get; set; }
    public async Task Confirm(bool value)
    {
        Show = false;
        OnResponseClick.InvokeAsync(value);
    }
    public void showPop()
    {
        Show = true;
        StateHasChanged();
    }

}

 Code Explanation

public bool Show { get; set; }

The Show property is a boolean indicating whether the modal should be displayed or not. It's used for conditional rendering of the modal.

[Parameter]
public string Title { get; set; } = "Delete Confirmation";
[Parameter]
public string massage { get; set; } = "Are you sure?";

These are the parameters for the component. Parameters allow you to pass values into the component from its parent. Title and message are used to customize the title and message displayed in the modal. The default values are set to "Delete Confirmation" and "Are you sure?".

[Parameter]
public EventCallback<bool> OnResponseClick { get; set; }

OnResponseClick is an event callback parameter. It allows the parent component to listen for events triggered by this component. The bool parameter indicates the response value (e.g., true for "Yes", false for "No").

public async Task Confirm(bool value)
{
    Show = false;
    OnResponseClick.InvokeAsync(value);
}

The Confirm method is called when the user clicks either the "Yes" or "No" button in the modal. It takes a boolean value as a parameter, sets Show to false (hiding the modal), and invokes the OnResponseClick event callback, passing the response value to the parent component.

public void showPop()
{
    Show = true;
    StateHasChanged();
}

The showPop method is responsible for showing the modal. It sets the Show property to true and calls StateHasChanged(). This method is presumably called when the parent component wants to display the modal.

Then when we click on the delete button this will call the DeleteRow method now write the DeleteRow() method.

UserDetailsModel userDetailsModel = new UserDetailsModel();
[Parameter] public Confirmation confirmation { get; set; } 
public void DeleteRow(UserDetailsModel userDetailsModel)
{
    try
    {
        UserID = userDetailsModel.UserId;
        confirmation.Title = "Delete Record";
        confirmation.massage = "Are your want to delete the Record?";
        confirmation.showPop();
    }
    catch(Exception ex) 
    {
        
    }   
}

 Code Explanation

UserID = userDetailsModel.UserId;

This line appears to set the UserID property (presumably a property of the class containing this method) to the UserId property of the userDetailsModel.

confirmation.Title = "Delete Record";

It sets the Title property of an object named confirmation to "Delete Record".

confirmation.massage = "Are you sure you want to delete the Record?";

It sets the message (probably a typo, should be the message) property of the confirmation object to the specified message.

confirmation.showPop();

It calls the showPop method on the confirmation object. This method presumably displays a modal or confirmation dialog, as suggested by the name.

UserDetailsModel userDetailsModel = new UserDetailsModel();

This line creates an instance of the UserDetailsModel class and assigns it to the variable userDetailsModel. This instance is likely used to pass information about the user details to the DeleteRow method.

[Parameter] public Confirmation confirmation { get; set; } 

This line declares a public property named confirmation of type Confirmation and marks it with the [Parameter] attribute. In Blazor, this indicates that the property can be set by a parent component.

Now, to display the confirmation modal popup upon clicking the delete button, we need to communicate between the UserInfo and Confirmation components. so write this code on UserInfo.razor component.

<Confirmation @ref="confirmation"></Confirmation>
@ref="confirmation"

The @ref directive is used to create a reference to the Confirmation component instance, and the reference is named "confirmation." This allows you to interact with the Confirmation component from the parent component's code.

Now. run your project and see the output.

blazor server

Then, to perform the delete operation in the radzen grid we need to perform EventCallback which I have already written in the Confirmation. razor page just needs to give the reference of this like this in the UserInfo.razor page

<Confirmation @ref="confirmation" OnResponseClick="@DeleteConfimation"></Confirmation>

Now write the DeleteConfimation method in the UserInfoBase.cs page.

public async Task DeleteConfimation(bool IsDeleted)
{
    try
    {
        if (IsDeleted)
        {
            await iuserDetails.DeleteRecord(UserID);
            userDetails = userDetails.Where(p => p.UserId != userDetailsModel.UserId).ToList();
            StateHasChanged();
        }
        else
        {
            StateHasChanged();
        }
    }
    catch(Exception ex)
    {

    }            
}

Create an interface of this method and write this code in IUserDetails.cs page.

public  Task<Int16> DeleteRecord(Int64 UserId);

Then implement this method in UserDetails.cs page.

public async Task<Int16> DeleteRecord(Int64 UserId)
{
    Int16 result = 0;
    try
    {
        using (var connection = _context.CreateConnection())
        {
            var procedure = "[Project_DeleteRecord]";

            var values = new { UserId = UserId };
            result = await connection.QueryFirstAsync<Int16>(procedure, values, commandType: CommandType.StoredProcedure);
        }

    }
    catch (Exception ex)
    {

    }
    return result;
}

Now run the project and see the output.

delete records

Conclusion

In this article, we have learned about how to create a reusable confirmation modal pop-up in a Blazor Server application using parameters and EventCallback. By implementing these concepts, developers can enhance user interaction, simplify code, and facilitate seamless communication between components in the Blazor framework.

FAQ's

Q 1.  What is a Parameter?

Ans. Parameters are values that are passed from a parent component to a child component. They allow you to communicate data downward in the component hierarchy. Parameters are unidirectional, meaning data flows from the parent to child components.

Q 2. What is EventCallback?

Ans. Event callbacks are used to handle events in a child component and notify the parent component about the occurrence of an event. Event callbacks are unidirectional as well, but they allow the child component to send messages or notify the parent component about some user interaction or state change.

Q 3. What is Blazor?

Ans. Blazor is an open-source web framework developed by Microsoft that allows developers to build interactive web applications using C# and .NET instead of JavaScript. Blazor enables full-stack web development with a single programming language and runtime.

Q 4. What are the two primary hosting models in Blazor?

Ans. There are two primary hosting models for Blazor.

Blazor WebAssembly

This model allows you to run Blazor applications directly in the web browser using WebAssembly, enabling code execution in the browser.

Blazor Server

In this model, the application's user interface is rendered on the server, and UI updates are sent to the client over a SignalR connection.


Similar Articles