Blazor Tutorial - How to create a modal component

Blazor modal tutorial



The following example illustrates how you can create a modal component in Blazor server. For the following example i have used mdbootstrap which can be found here www.mdboostrap.com
The goal is to create a modal that is easy to use and which can be used as a template across your site.



First make sure you have added the mdboostrap cdn to _host.cshtml file:

<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BlazorModalApp</title>
<base href="~/" />
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link href="css/site.css" rel="stylesheet" />

<!-- Font Awesome -->
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css">
<!-- Google Fonts -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap">
<!-- Bootstrap core CSS -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css" rel="stylesheet">
<!-- Material Design Bootstrap -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/mdbootstrap/4.19.0/css/mdb.min.css" rel="stylesheet">
</head>

and the scripts:

    <script src="_framework/blazor.server.js"></script>

<!-- JQuery -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!-- Bootstrap tooltips -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.4/umd/popper.min.js"></script>
<!-- Bootstrap core JavaScript -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.min.js"></script>
<!-- MDB core JavaScript -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mdbootstrap/4.19.0/js/mdb.min.js"></script>


First thing first. Create a new razor component:


 I called mine DefaultModal as i want to be able to create multiple different kinds of modals depending on the style. Add your app namespace to the top of the file.

@namespace BlazorModalApp

<h3>Default Modal</h3>


@code {

}


For this tutorial, i have chosen the first modal in mdb arsenal.

<!-- Button trigger modal -->
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#basicExampleModal">
Launch demo modal
</button>

<!-- Modal -->
<div class="modal fade" id="basicExampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>

Copy the code into your new razor compoent. The code should look something like this:


@namespace BlazorModalApp.Pages

<!-- Button trigger modal -->
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#basicExampleModal">
Launch demo modal
</button>

<!-- Modal -->
<div class="modal fade" id="basicExampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>

@code {

}



if you reference the app on the index page and launch the app, it should be possible to see the modal in action, the reference should look something like this:

@page "/"

<h1>Hello, world!</h1>

Welcome to your new app.

<SurveyPrompt Title="How is Blazor working for you?" />

<DefaultModal/>


At launch, you can now see the modal button, if you press it the modal will show:


You can now fiddle with it your self, or continue this tutorial to make the modal more generic to allow multiple usage.


The modal should be able to have different content without you editing the modal it self. To do this, we add the components child content to the modal body. But before we do that, the modal has to be configured so it can be used properly.



First remove the button, we will fire that seperately.
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#basicExampleModal">
Launch demo modal
</button>

Add the following to the code section:

[Parameter]
public dynamic Parent { get; set; }

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

public string ModalTitle { get; set; }
public string SaveChangesButtonText { get; set; } = "Save";
public bool ShowFooterButtons { get; set; } = true;

public readonly Guid Guid = Guid.NewGuid();
private string modalDisplay = "none;";
private string modalClass = "";
private bool showBackdrop;
public string ErrorMessage = "";

To explain a bit about the above code.
The dynamic Parent property is used to reference its parent, without knowing its type. This way, its possible to call methods on its parent if it exists. The RenderFragment contains the markup we want the modal to contain. Modal title is selfexplainatory. SaveChangesButtonText gives the possibility to change the save button text, you can also disable it all together and place your own button inside the modal by disable ShowFooterButtons. In order to be able to have multiple modals, each instance should have a unique id, this is done by creating a Guid. modalDisplay, modalClass, showBackdrop is used to handle how and when the modal is displayed. And lastly, i inserted a ErrorMessage string which can be set from parent in case we want to display a error massage in the modal whenever the user clicks ie. the save button.

Lets implement the properties


<div class="modal @modalClass" id="@Guid" tabindex="-1" role="dialog" style="display: @modalDisplay" aria-labelledby="exampleModalCenterTitle"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
@if (!string.IsNullOrWhiteSpace(ModalTitle))
{
<h5 class="modal-title">@ModalTitle</h5>
}

<button type="button" class="close" data-dismiss="modal" data-target="@Guid" aria-label="Close" @onclick="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
@ChildContent
<p class="text-danger font-weight-bold">@ErrorMessage</p>
</div>
@if (ShowFooterButtons)
{
<div class="modal-footer">
<button type="button" class="btn btn-outline-primary" @onclick="CallParentApplyMethod">@SaveChangesButtonText</button>
<button type="button" class="btn btn-outline-default" data-dismiss="modal" data-target="@Guid" @onclick="Close">Close</button>
</div>
}
</div>
</div>
</div>

@if (showBackdrop)
{
<div class="modal-backdrop fade show"></div>
}

Walkthrough:
  • We want the @modalClass to display the modal on trigger.
  • The id will be set to the @Guid, which also will be used as data-target to ensure we are referencing the correct modal.
  • The title will only display if a titel has been set using if statement and @ModalTitle.
  • modal-body contains the @ChildContent, which will have the html content of the modal.
  • I added a Error message in the bottom of the modal-body to be able to display error messages.
  • After the body, a if statement is made whether or not the footer should be shown using "ShowFooterButtons".
  • The save button will call a method called "CallParentApplyMethod" where the text of the button is either "Save" or what is being specified.
  • The close button will call a close method and have the data-target set to the Guid.
  • The last if statement shows the backdrop of the modal.

The modal should have some methods for handing opening and closing of the modal, and the possibility of calling back to its parent. Add the following code to the modal:




private void CallParentApplyMethod()
{
if (Parent.Apply())
{
Close();
}
}

public void Open()
{
modalDisplay = "block;";
modalClass = "Show";
showBackdrop = true;
StateHasChanged();
}

public void Close()
{
modalDisplay = "none";
modalClass = "";
showBackdrop = false;
ErrorMessage = "";
StateHasChanged();
}

Walkthrough from bottom to top:

  • The "Close" method is public so it can be called from its parent. It sets the modal display to none, so the modal is not shown, the modal class has removed the attribute "show", the backdrop is likewise removed and the errormessage is being cleared if any. Then a force of render is being invoked.
  • The "Open" method enables the modal by setting the display to "block;" and class to "Show" while setting the backdrop to true. Then it rerenders.
  • The intersting part of the code is the "CallParentApplyMethod". It takes the parent as a dynamic and tries to run a method called "Apply". If apply returns true, it will close the modal. I have chosen not to do check for if the method exists. But if you are going to use the modal multiple times on a page, it might not be suitable to call the same method for all the modals. There is different approaches to this. The first one would be to check if the method exist.
public static bool HasMethod(this object objectToCheck, string methodName)
{
    var type = objectToCheck.GetType();
    return type.GetMethod(methodName) != null;
}
The second one is to run a method of your choice by having a setting the method name it should call. This could be a string. Again, i have chosen not to do here for simplicity purpose.


The modal is done, and we can test it on our index page by adding a button which has to be pressed before the modal is shown, and the content of the modal inside the modal component:


@page "/"

<h1>Hello, world!</h1>

Welcome to your new app.

<SurveyPrompt Title="How is Blazor working for you?" />


<button class="btn btn-primary" @onclick="ShowModal">test me</button>

<DefaultModal @ref="defaultModal" Parent="@this">

<p>This is our modal content</p>

</DefaultModal>




@code
{
DefaultModal defaultModal;


private void ShowModal()
{
defaultModal.Open();
}

public bool Apply()
{
return true;
}
}


If we run the app, it will look like this:














the complete code can be found here:



@namespace BlazorModalApp.Pages

<div class="modal @modalClass" id="@Guid" tabindex="-1" role="dialog" style="display: @modalDisplay" aria-labelledby="exampleModalCenterTitle"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
@if (!string.IsNullOrWhiteSpace(ModalTitle))
{
<h5 class="modal-title">@ModalTitle</h5>
}

<button type="button" class="close" data-dismiss="modal" data-target="@Guid" aria-label="Close" @onclick="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
@ChildContent
<p class="text-danger font-weight-bold">@ErrorMessage</p>
</div>
@if (ShowFooterButtons)
{
<div class="modal-footer">
<button type="button" class="btn btn-outline-primary" @onclick="CallParentApplyMethod">@SaveChangesButtonText</button>
<button type="button" class="btn btn-outline-default" data-dismiss="modal" data-target="@Guid" @onclick="Close">Close</button>
</div>
}
</div>
</div>
</div>

@if (showBackdrop)
{
<div class="modal-backdrop fade show"></div>
}

@code {

[Parameter]
public dynamic Parent { get; set; }

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

public string ModalTitle { get; set; }
public string SaveChangesButtonText { get; set; } = "Save";
public bool ShowFooterButtons { get; set; } = true;

public readonly Guid Guid = Guid.NewGuid();
private string modalDisplay = "none;";
private string modalClass = "";
private bool showBackdrop;
public string ErrorMessage = "";

private void CallParentApplyMethod()
{
if (Parent.Apply())
{
Close();
}
}

public void Open()
{
modalDisplay = "block;";
modalClass = "Show";
showBackdrop = true;
StateHasChanged();
}

public void Close()
{
modalDisplay = "none";
modalClass = "";
showBackdrop = false;
ErrorMessage = "";
StateHasChanged();
}

}





Le me know if you want more blazor component tutorials.

Comments

Popular posts from this blog

[SOLVED] Veeam: Failed change tracking (CBT)