20

I am trying to show bootstrap modal then bind its buttons. But I cannot pass the first step showing the modal. I am using Blazor client template of .net core 3.1. I have a page named Modal.razor which contains the bootstrap modal I found from getbootstrap.com.

@if (Show)
{
    <div class="modal" tabindex="-1" role="dialog">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">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">
                    <p>Modal body text goes here.</p>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-primary">Save changes</button>
                    <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                </div>
            </div>
        </div>
    </div>
}
@code {
    [Parameter]
    public bool Show { get; set; } = false;
}

An I called the modal in the index.razor file

@page "/"

<button @onclick="(()=>switchModal=!switchModal)">Switch Modal</button>

<Modal Show="switchModal"/>

@code{
    bool switchModal = false;
}

You might say StateHasChanged should be called here. But even if I copy and paste the modal code in the index.razor, I won't see anything.

Sorush
  • 1,384
  • 3
  • 14
  • 27
  • The problem with your code is, that all you're doing is switching whether the HTML is being sent to the client or not, but with bootstrap, the modal HTML is always on the page, and is either triggered with javascript with $('#modal').modal() or with a data-toggle and data-target tag on the button that should open it. – Kyle Dec 09 '19 at 21:29
  • 1
    You can use this Nuget package: https://www.nuget.org/packages/Majorsoft.Blazor.Components.Modal/ It supports backdrop and animation as well... Docs: https://github.com/majorimi/blazor-components/blob/master/.github/docs/Modal.md – Major Nov 16 '20 at 19:31

6 Answers6

45

There is likely a better way to do this, but here's a working example to get you started:

Page:

@page "/modal-test"

<BlazorApp1.Components.Modal @ref="Modal"></BlazorApp1.Components.Modal>

<button @onclick="() => Modal.Open()">Open Modal</button>

@code {
    private BlazorApp1.Components.Modal Modal { get; set; }
}

Component:

<div class="modal @ModalClass" tabindex="-1" role="dialog" style="display:@ModalDisplay">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">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">
                <p>Modal body text goes here.</p>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-primary">Save changes</button>
                <button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="() => Close()">Close</button>
            </div>
        </div>
    </div>
</div>


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


@code {


  public Guid Guid = Guid.NewGuid();
    public string ModalDisplay = "none;";
    public string ModalClass = "";
    public bool ShowBackdrop = false;

    public void Open()
    {
        ModalDisplay = "block;";
        ModalClass = "Show";
        ShowBackdrop = true;
        StateHasChanged();
    }

    public void Close()
    {
        ModalDisplay = "none";
        ModalClass = "";
        ShowBackdrop = false;
        StateHasChanged();
    }
}

Another option to go about this, would be to use JSInterop to call $('#modalId').modal()

You could have each version of the component have a unique id by doing something like this: <div id="bootstrap-modal-@Guid" then use the saved ID to call .modal() with jQuery.

Kyle
  • 28,411
  • 33
  • 120
  • 167
  • Great, works well. Do you know how can I make the background except modal window dark? – Sorush Dec 10 '19 at 18:18
  • 1
    @Sorush I updated my answer to fix that. Bootstrap 4 looks like it's adding a `` to darken behind the modal, so I used a boolean to show or hide it. – Kyle Dec 10 '19 at 18:31
  • Could you exemplify how to get the user's answer from the modal dialog into some variable in the modal-test page? I'm still not real clear on blazor data binding. – Linus Proxy Feb 12 '20 at 18:12
  • This has worked great for me for most of my modals, but now I have a really long modal and I would like it to scroll like this one: https://getbootstrap.com/docs/4.4/components/modal/#scrolling-long-content Instead this is too big for the window and only the content behind the modal scrolls from mouse wheel. Any suggestions? – Matt Sanders Mar 18 '20 at 09:24
  • 1
    Great, but not work with `fade` class and why use you `Guid` ? i mean `public Guid Guid = Guid.NewGuid();` – Zanyar J.Ahmed Jul 02 '20 at 20:59
  • Hi @ZanyarJ.Ahmed The reason for using a Guid in this example component is explained in the last line. It is only if you need a unique id on the modal to do something with it with jquery. – Kyle Jul 06 '20 at 01:06
  • @ZanyarJ.Ahmed Can you elaborate on "not work with fade class"? What issue are you having? – Kyle Jul 06 '20 at 01:06
  • 1
    I mean your code not detected `fade` css class *Not work animation* and if use large `Model` not work scroll – Zanyar J.Ahmed Jul 06 '20 at 07:02
  • Great easy way to this problem. Good that no external nuget package is used. Thanks @Kyle. – Tajveer Singh Nijjar Aug 10 '20 at 02:53
  • 1
    @ZanyarJ.Ahmed Have you found the solution to why the `fade` class is not using? – TanvirArjel Dec 07 '20 at 03:11
  • yep i also tried alot but still no fade efect - does someone have any solution? – d00lar Feb 03 '21 at 11:24
23

Building on Kyle's answer, this is my first experiment with Blazor: Making the modal dialog component take any markup or component.

Modal.razor

<div class="modal @modalClass" tabindex="-1" role="dialog" style="display:@modalDisplay; overflow-y: auto;">
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">@Title</h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                @Body
            </div>
            <div class="modal-footer">
                @Footer
            </div>
        </div>
    </div>
</div>

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

@code {
    [Parameter]
    public RenderFragment Title { get; set; }

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

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

    private string modalDisplay = "none;";
    private string modalClass = "";
    private bool showBackdrop = false;

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

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

Index.razor

@page "/"

<h1>Hello, world!</h1>

Welcome to your new app.
<button class="btn btn-primary" @onclick="() => modal.Open()">Modal!</button>

<Modal @ref="modal">
    <Title>This is a <em>Title!</em></Title>
    <Body>
        <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Omnes enim iucundum motum, quo sensus hilaretur.
            <i>Quis istud possit, inquit, negare?</i>
            <mark>Ego vero isti, inquam, permitto.</mark> Duo Reges: constructio interrete.
        </p>
        <FetchData />
        <dl>
            <dt><dfn>Stoici scilicet.</dfn></dt>
            <dd>An hoc usque quaque, aliter in vita?</dd>
            <dt><dfn>Erat enim Polemonis.</dfn></dt>
            <dd>Quod cum accidisset ut alter alterum necopinato videremus, surrexit statim.</dd>
        </dl>
    </Body>
    <Footer>
        <button type="button" class="btn btn-primary">Save changes</button>
        <button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="() => modal.Close()">Close</button>
    </Footer>
</Modal>

@code {
    private Modal modal { get; set; }
}
grammophone
  • 391
  • 3
  • 6
5

Also building on Kyle's answer, you can sustain the bootstrap fade effect if you place a short delay between the display and class adjustments.

@code {

    ...

    public async Task OpenModal()
    {
        ModalDisplay = "block;";
        await Task.Delay(100);//Delay allows bootstrap to perform nice fade animation
        ModalClass = "show";
        StateHasChanged();
    }

    public async Task CloseModal()
    {
        ModalClass = "";
        await Task.Delay(250);
        ModalDisplay = "none;";
        StateHasChanged();
    }
}

I also applied the ModalClass and ModalDisplay variables to the backdrop element too

<div class="modal-backdrop fade @ModalClass" style="display: @ModalDisplay"></div>

I believe bootstrap can better identify the state change that triggers the animation this way

Daniel
  • 63
  • 1
  • 6
1

for backdrop shadow only add fade class:

<div class="modal fade @ModalClass" tabindex="-1" role="dialog" 
     style="display:@ModalDisplay">
0

Kyle's components work well but does anyone know how to add draggable and resizable features to a bootstrap modal using the jqueryUi draggable()/resizeable() functions?

I have this link to a pure javascript solution: DRAG AND RESIZE BOOTSTRAP MODAL that essentially calls the resizeable and draggable functions on the modal divs

<script src="https://code.jquery.com/ui/1.11.3/jquery-ui.min.js"></script>
<script type="text/javascript">
    $('.modal-content').resizable({
        //alsoResize: ".modal-dialog",
        minHeight: 300,
        minWidth: 300
    });
    $('.modal-dialog').draggable();
</script>

I've tried adding this script to my _Host.cshtml page but it has no effect. Any advice on how to do this would be gratefully received...

David

Updated with answer

The answer is to explicitly call a javascript function in the OnAfterRenderAsync override to apply the JQuery UI functions to the modal divs.

E.g.

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await jsRuntime.InvokeVoidAsync("setModalDraggableAndResizable");
        await base.OnAfterRenderAsync(firstRender);
    }

where setModalDraggableAndResizable is a javascript function in the _Hosts.cshtml:

    <script src="https://code.jquery.com/ui/1.11.3/jquery-ui.min.js"></script>
    <script type="text/javascript">
        function setModalDraggableAndResizable() {
            $('.modal-content').resizable({
                //alsoResize: ".modal-dialog",
                minHeight: 300,
                minWidth: 300
            });
            $('.modal-dialog').draggable();
        }
    </script>

And the modal is now draggable and resizable...

Modal example image

0

With Kyle solution my Dialog do not close when i click on the backdrop.

I saw that it is a problem of z-index: the modal div has a z-index of 1050, and the backdrop div has 1040, in this way i was not able to click my backdrop.

I have moved the backdrop inside the dialog div and added to the modal-dialog div z-index > 1040 (ES: 1055)

I also added data-dismiss="modal" @onclick="() => Close()" to the backdrop div and now it works as well as the "Close" button.

<div class="modal @ModalClass" tabindex="-1" role="dialog" style="display:@ModalDisplay">

    <div class="modal-dialog" role="document" style="z-index:1055">
       ...
    </div>    

@if (ShowBackdrop)
{
    <div class="modal-backdrop fade show"  data-dismiss="modal" @onclick="() => Close()"></div>
}

</div>
Farest
  • 1
  • 2