friggle
friggle

Reputation: 3471

DOM placement of Bootstrap modals in Partial Views

I have a button element that triggers a modal. This piece of code is reusable, and needs to be present on many different views.

<button class="btn btn-default" type="button" data-toggle="modal" data-target="#modalAcctToggle">
    Toggle Accounts
</button>

<div id="modalAcctToggle" tabindex="-1" class="modal fade">
    <div class="modal-dialog modal-sm">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
                <h4 class="modal-title">Available Accounts</h4>
            </div>
            <div class="modal-body">
                [contents not relevant]
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
            </div>
        </div>
    </div>
</div>

Pretty simple and straightforward. Put all that in a partial view, and render it as necessary with @Html.Partial("_accountToggler")

However, Bootstrap documentation encourages placing modals outside of the meat of the page:

http://getbootstrap.com/javascript/#modals

Modal markup placement Always try to place a modal's HTML code in a top-level position in your document to avoid other components affecting the modal's appearance and/or functionality.

My immediate thought was to use Razor sections for this.

<button data-target="#modalAcctToggle" etc... />
@section modals {
    <div id="modalAcctToggle" etc... />
}

Wrap all modals in a section, then use @RenderSection("modals") in the Layout, and the modals would be emitted at a safe place in the DOM.

Unfortunately, Razor sections do not work in Partial Views! This is apparently by design, but it is meant to limit the use of scripts in partials.

I have found many other questions from people trying to work around the lack of sections in partials, but their exclusive focus is on dealing with scripts. The solutions found there essentially consist of rolling your own equivalent to Razor sections, or are specialized for dealing with scripts (like bundling). It seems ludicrous to have to put in the effort of reinventing Razor sections to hack around an intentional limitation when my usage is not the target of said limitation.

How can I best handle getting the modal HTML into a different spot?

UPDATE: As discussed in the comments on the answer, this question was based in two fundamental misunderstandings.

1) Bootstrap modals can be located amongst the rest of the HTML without issue. You just have to be careful to keep it from inheriting any CSS it shouldn't.

2) Sections are not cumulative. So even if I could use sections in partial views, it would not work the way I want it to. Multiple partials using the same section would overwrite each other. This is likely the real reason why, by design, sections do not work in partials.

Upvotes: 1

Views: 1579

Answers (1)

Tieson T.
Tieson T.

Reputation: 21191

TL;DR; The key takeaway here is that sections are defined in the layout view; they're roughly equivalent to the ContentPlaceholder controls you'd find in a WebForms project. They are then used in the "full" views that normally correspond to your various actions. If you haven't read it yet, I'd say Scott Gu's intro to sections has a good explanation.


If we assume that you have this partial view, which I'll call _Modal.cshtml:

<button class="btn btn-default" type="button" data-toggle="modal" data-target="#modalAcctToggle">
    Toggle Accounts
</button>

<div id="modalAcctToggle" tabindex="-1" class="modal fade">
    <div class="modal-dialog modal-sm">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
                <h4 class="modal-title">Available Accounts</h4>
            </div>
            <div class="modal-body">
                [contents not relevant]
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
            </div>
        </div>
    </div>
</div>

And you have a layout similar to

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - My ASP.NET Application</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")

</head>
<body>
    @RenderSection("modals", required: false)

    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - My ASP.NET Application</p>
        </footer>
    </div>

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")

    @RenderSection("scripts", required: false)
</body>
</html>

The you can do something like this in, say, Index.cshtml

@section modals
{
    @Html.Partial("_Modal")
}

and it will emit the proper markup in the correct place. You would then need to place any interaction logic either directly in the parent view, or use the scripts section (in the parent view).

Upvotes: 3

Related Questions