How to Build A Reusable Dialog With Blazor
Tags: css, webdev, csharp, dotnet
Published: 2023-08-28 17:34:57

## Introduction 👋🏻
Hello there! I got an interesting tutorial for you in this post. Here, we'll be building a nice looking, fully reusable dialog component using Blazor for interactivity, and plain css for styling, the reason I didn't use something like Tailwind Css is because you'll have to add Tailwind to the projects where you want to use the dialog, so for that, I went with standard Css so you can subsequently put the dialog in a **Razor Class Library**, just reference the library from your Blazor project, and add the component where ever you like, and it'll work straight out of the box.

> 🌟Note: If you want to get the source code for the component, you can check out the Github repository for this post on my Github here: https://github.com/rasheed-k-mozaffar/CoolDialog

## Beginning The Project ➕
For the sake of simplicity, I created a new **Blazor WebAssembly Empty App** project to use for this tutorial, now if you want to follow along exactly what I'll be doing, then begin by doing the following:
- Create an empty Blazor Wasm project
- Enable Css isolation by uncommenting this statement inside **index.html**, the root file inside wwwroot:

```html

<link href="CoolDialog.styles.css" rel="stylesheet" />

```

> 💡 I named my project CoolDialog, hence the reason you're seeing it in the code, this line comes commented out by default, so if you want to enable Css isolation, make sure to uncomment it.

- Add the CDN link for Bootstrap Icons

> 💡 This step is optional, but I wanted to use an icon for the close dialog button, which you'll see in a minute, but if you want to follow along, head to https://icons.getbootstrap.com/ ,scroll all the way down, copy the CDN Link, and paste it in the head of **index.html**

The head of **index.html** should look something like this:

```html

<head>
    <meta charset="utf-8" />
    <title>CoolDialog</title>
    <base href="/" />
    <link href="css/app.css" rel="stylesheet" />

    <!--Add Bootstrap Icons CDN Link-->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
    <!--Enable Css Isolation-->
    <link href="CoolDialog.styles.css" rel="stylesheet" />
</head>

```

- Because we like clean structure, we'll create a new folder called **Components**, it'll be the place in which we save **Razor Components** along with their **Component Scoped Css** files.

The project structure should look something like this:

![Project files structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vhbbqyn5baeb4pe3zq0x.png)

## We're Ready to Start 🚀
Inside our Components directory, create two files, one for the dialog, and another for its styles.

We'll begin by adding the Html skeleton of the dialog, add this following markup to the Dialog.razor component:

```html

<div class="overlay">
    <div class="dialog">
        <div class="dialog-header">
                <div> </div>

                <div>
                    <div class="close">
                        <i class="bi bi-x-lg"> </i>
                    </div>
                </div>
        </div>

        <div class="dialog-body">

        </div>

        <div class="dialog-footer">

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

```

We have couple of sections here:
**1:** Overlay: Everything surrounding the dialog box.
**2:** Dialog: The container of the dialog itself.
**3:** Dialog Header: The top portion of the dialog.
**4:** Dialog Body: The main content of the dialog.
**5:** Dialog Footer: Action buttons or just empty.

Let's add the Css for it, the following snippet shows the styling for the dialog:

```css

.overlay {
    position: fixed;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    background-color: rgba(0,0, 0, 0.5);
    display: flex;
    justify-content: center;
    align-items: center;
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}

.dialog {
    width: 750px;
    background-color: white;
    border-radius: 10px;
    padding: 1rem;
}

    .dialog .dialog-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 2rem;
    }

    .dialog .dialog-body {
        padding: 2rem;
    }

    .dialog .dialog-footer {
        padding: 2rem;
    }


.close {
    height: 40px;
    width: 40px;
    background-color: transparent;
    transition: 0.25s ease;
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}

    .close:hover {
        background-color: #DDD;
    }


```

> 💡 Feel free to review the Css and change it according to your needs, and also, if you want to also control the css while reusing the component, you can use some inline styles and dynamic values for them using C#.

Pay attention to these lines:

```css

backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);

```
The reason for the second line is because **Safari** requires it and without it, the **blur** won't appear if you're going to use Safari. However, it works just fine on Edge and Chrome, so make sure to add it as it gives the overlay a nice look to it.

## Time To Make Things Interactive 👀
So far all we've done was plain Html and Css, now we'll add some C# to add interactivity to our dialog, and we'll make it reusable by using the **RenderFragment** type, which will allow us to reuse the dialog, and based on the need, we'll be able to rebuild the inner parts of it, with the footer as well, so let's get going.

To begin with, we'll need a bunch of parameters for our dialog, like the title, is it opened or not, a UI segment for the body and another for the footer, so add a code directive to the **Dialog.razor** component file, and inside it, add the following parameters:

```csharp

@code {
    [Parameter] public string? Title { get; set; }
    [Parameter] public RenderFragment? BodyContent { get; set; }
    [Parameter] public RenderFragment? FooterContent { get; set; }
    [Parameter] public bool Opened { get; set; }
    [Parameter] public EventCallback CloseButtonClicked { get; set; }

    private void CloseDialog()
    {
        Opened = false;
        CloseButtonClicked.InvokeAsync();
    }
}

```
Before we talk about the previous code, create an if statement that wraps all of the dialog markup:

```csharp

@if(Opened)
{
    // Dialog markup goes here
}

```

This will check if **Opened** equals true, then the dialog will be displayed, otherwise, the dialog will not appear.

For the code written in the code directory, let's see what it does. Ignore the **CloseDialog** method for now, and focus on the parameters, the **title** is a simple string, the **BodyContent** and **FooterContent** are of type **RenderFragment**, this allows to write some markup inside the dialog component where we're trying to render it, which gives the flexibility to reuse the same component, in different places, while keeping the general functionality present, like opening the dialog, closing it, and its styles as well. 
Now we need to plug those parameters into the Html. For brevity, I'll show snippets for each section of the dialog!

**The header section**

```html

<div class="dialog-header">
    <div>
        <h2>@Title</h2>
    </div>

    <div @onclick="CloseDialog">
        <div class="close">
            <i class="bi bi-x-lg"> </i>
        </div>
    </div>
</div>

```
 **The body section**

```html

<div class="dialog-body">
    @BodyContent
</div>

```

And lastly, **the footer section:**
```html

<div class="dialog-footer">
    @FooterContent
</div>

```

For the close button, we wired an event for when the button is clicked, what's going to happen is, the dialog will close as **Opened** will be turned into **false**, therefore the condition inside the main if statement will result in false hence it won't render the dialog, and the event callback will be fired, but this one is for later use, we'll talk about it at a later time.

And our dialog is now ready to be used, let's first add a way to open it when we want, we'll do that inside **Index.razor**, the home page in our project.

The following is the content of **Index.razor**:

```html

@page "/"

<h1>Hello, world!</h1>

<button class="cool-button" @onclick="ShowDialog">Open Dialog</button>

<Dialog Title="Hello World!" Opened="dialogShown">
    <BodyContent>
        <p style="font-size: 13px;">
            Lorem, ipsum dolor sit amet consectetur...
        </p>
    </BodyContent>

    <FooterContent>
        <button class="ok-button">Ok</button>
    </FooterContent>
</Dialog>

@code {
    private bool dialogShown = false;

    private void ShowDialog()
    {
        dialogShown = true;
    }
}

```

The code adds a button and hooks it to an event, when clicked, the dialog should open, then I'm adding the component itself, providing it with the required parameters like **Title**. The parameter **Opened** will be provided according to a a value controlled by a private variable **dialogShown**, this variable will control wether we want to open the dialog or not.
Inside the dialog, we have this markup:

```html

<BodyContent>
    // paragraph
</BodyContent>

<FooterContent>
    // Ok button
</FooterContent>

```

These are the Render fragments that will allow us to control the content of the dialog, inside those tags, you can add whatever you want, and that's how we can make a reusable component, I just added a simple paragraph and a button that does nothing, but if you want, you could add an entire form inside the dialog, and add more action buttons like **Submit**, **Cancel**, and links to other pages or actions.

## How To Handle The Closing of The Dialog 🚫
Remember that event callback we added as a parameter, that event could help us clear things up inside the dialog. Assuming you have a form inside the dialog, and the user typed in some stuff inside the fields, but then decided to cancel, and closed the dialog, for that reason, we should have a backing event that we can use to clear those fields, and make sure our dialog is closed properly, now here I don't have a form inside my dialog, the thing I want to do here, is hook that event, so that the variable **dialogShown** reverts back to false when the **CloseButtonClicked** event fires.

I can do that by providing an event for the parameter, like the following:

```html

<Dialog Title="Hello World!" Opened="dialogShown" CloseButtonClicked="@(() => dialogShown = false)">

```

This will ensure that my variable is restored to its original state, and that is **false**.

## Result 🧠

![Final result of the dialog component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u0u8sc8pkg2yeg0f3w4i.png)

## Conclusion
In this post, you've learned how create a reusable dialog but not only that, you got a component that you could use in many upcoming projects, all you have to do is tweak it a little to fit the criteria of a project, and it'll work just fine, additionally, you've seen how we can make a reusable component using things like **RenderFragment** and component parameters, if you have any questions or suggestions, make sure to leave them in the comments down below!


## Bye for now 👋🏻