7

I have a simple page index.razor with a button:

<a class="btn btn-login" @onclick="RedirectPage" >Log in</a>

<div
    @bind-Visible="@InvalidLogin"
    BodyText="Error">
</div>

@code{
    InvalidLogin {get; set;} = false;
}

Where the function RedirectPage checks if values are valid. If they are not, I want a popup giving information:

private void RedirectPage
{
    this.InvalidLogin = true;
}

This function is in the index.razor.cs and has been added with @using in the correct namespace.

How can I create it so that a popup shows up whenever the button is clicked?

2
  • When you say "popup" are you talking about a modal dialog? If so, which CSS framework are you using? If so, check out this answer and my answer, stackoverflow.com/questions/71882886/… Commented Apr 25, 2022 at 20:04
  • Understand that on the client side a pop-up is just a div or other element that is styled to appear in front of other elements. One very simple way of achieving this is to put a div in your Layout page. You can pass your layout page to all its children using the “this” keyword, And then use a method on the layout page to change the CSS class of the pop-up element. Commented Apr 26, 2022 at 15:00

4 Answers 4

18

You can create a simple popup (or modal dialog) component. Below, I wrote a sample popup razor component using Bootstrap 5 toast component.

Popup.razor file

@{
    var showClass = IsVisible ? "d-block" : "d-none";
}

<div class="toast-container p-3 @showClass" data-bs-autohide="true" data-bs-delay="5000">
    <div class="toast show" role="alert" aria-live="assertive" aria-atomic="true">
        <div class="toast-header">
            <strong class="me-auto">@HeaderText</strong>
            <button type="button" class="btn-close" aria-label="Close" @onclick="Close"></button>
        </div>

        <div class="toast-body">
            @BodyText
        </div>
    </div>
</div>

@code {
    [Parameter]
    public bool IsVisible { get; set; }

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

    [Parameter]
    public string? HeaderText { get; set; }

    [Parameter]
    public string? BodyText { get; set; }

    public void Show(string bodyText, string headerText = "")
    {
        HeaderText = headerText;
        BodyText = bodyText;
        IsVisible = true;
        StateHasChanged();
    }

    private void Close()
    {
        HeaderText = string.Empty;
        BodyText = string.Empty;
        IsVisible = false;
        StateHasChanged();
    }
}

Using the Popup razor component in your code:

<a class="btn btn-login" @onclick="RedirectPage" >Log in</a>

<Popup @ref="popupRef" />

@code{
    private Popup popupRef;
    
    private void RedirectPage()
    {
        // Shows the popup at the center of the screen
        popupRef.Show("Popup body text");
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, with some changes I got this to work.
Thanks for your simple way, how to close it when user clicks outside of popup?
7

How to create a dialog without a dependency on a third party library.

I had to use a minimal amount of js as the new HTML5 <dialog... element can only be opened in dialog mode with it .showModal() not by manipulating attributes.

wwwroot/scripts/dialogJsInterop.js

export function showDialog(element, parm) {
    return element.showModal();
}

export function closeDialog(element, parm) {
    return element.close();
}

Dialog.razor

<CascadingValue Value=@this IsFixed=true >
    <dialog @ref="@dialogElement" @attributes=@CapturedAttributes>
    @if(visible)
    {
        @ChildContent
    }
    </dialog>
</CascadingValue>

Dialog.razor.cs

public partial class Dialog : ComponentBase, IAsyncDisposable
{
    private readonly Lazy<Task<IJSObjectReference>> moduleTask;
    private ElementReference dialogElement;
    private bool visible = false;

    public Dialog()
    {
        moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>(
            identifier: "import",
            args: "./scripts/dialogJsInterop.js")
        .AsTask());
    }

    [Inject]
    private IJSRuntime jsRuntime { get; set; }

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

    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object> CapturedAttributes { get; set; }

    public async ValueTask ShowDialogAsync()
    {
        var module = await moduleTask.Value;
        await module.InvokeVoidAsync("showDialog", dialogElement);
        visible = true;
    }

    public async ValueTask CloseDialogAsync()
    {
        var module = await moduleTask.Value;
        await module.InvokeVoidAsync("closeDialog", dialogElement);
        visible = false;
    }

    public async ValueTask DisposeAsync()
    {
        if (moduleTask.IsValueCreated)
        {
            var module = await moduleTask.Value;
            await module.DisposeAsync();
        }
    }
}

A this stage you have a dialog that works.

I added the following components to make it more convenient. Note: I do use bootstrap from here forward for styling, this could be changed easily to tailwind for example.

DialogCloseButton.razor

<button @attributes=CapturedAttributes @onclick=@CloseDialog />

DialogCloseButton.razor.cs

public partial class DialogCloseButton : ComponentBase
{
    [CascadingParameter]
    public Dialog Dialog { get; set; }

    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object> CapturedAttributes { get; set; } = new Dictionary<string, object>
    {
        { "class", "btn btn-close" }
    };

    private async Task CloseDialog() => await Dialog.CloseDialogAsync();
}

DialogCloseButton.razor.css

.btn:focus {
    box-shadow: none;
}

DialogLayout.razor

<div class="d-flex flex-row justify-content-between border-bottom border-1">
    <div class="flex-fill p-1 ps-3 fw-bolder user-select-none app-gradient text-white">
        @Header
    </div>
    <div class="p-1">
        <DialogCloseButton />
    </div>
</div>
<div class="p-3">
    @Content
</div>

DialogLayout.razor.cs

public partial class DialogLayout
{
    [Parameter]
    public RenderFragment Header { get; set; }

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

Usage :

<Dialog @ref=@dialog class="p-0 border rounded shadow">
    <DialogLayout>
        <Header>
           <MessagesIcon Size=16 /> Add Message
        </Header>
        <Content>
            <MessageFormView />
        </Content>
    </DialogLayout>
</Dialog>
<button class="btn btn-outline-success" @onclick=@OpenDialog>Add Message</button>
@code {
    private Dialog dialog;

    ...

    private async Task OpenDialog() => await dialog.ShowDialogAsync();
}

Comments

2

Here is a very minimal example of what you ask (I put everything in the index.razor file, but you can use CSS isolation and a dedicated .cs file for all the content inside the @code{} part.

@page "/index"
<style>
    .active {
        display: block;
    }
    .inactive {
        display: none;
    }
</style>

<a class="btn btn-login" @onclick="RedirectPage" >Log in</a>

<div class="@PopupClass">
    Error: @ErrorText
</div>

@code{
    bool InvalidLogin {get; set;} = false;

    string PopupClass => InvalidLogin ? "active" : "inactive";
    public string ErrorText { get; set; } = "Example of exception";

    private void RedirectPage()
    {
        this.InvalidLogin = !this.InvalidLogin;
    }
}

Of course you will need to appropriate yourself this example in order to implement more concrete business logic.

Comments

0

There is not necessary to create your own popup logic from scratch - many libraries with components available in NuGet Package Manager. Just open it and search 'blazor' - and you can find many already created components. As example - Syncfusion.Blazor.Popups.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.