DmitryEfimenko / TwitterBootstrapMvc

Fluent implementation of ASP.NET-MVC HTML helpers for Twitter Bootstrap.
Apache License 2.0
223 stars 79 forks source link

Load modal content via Ajax #300

Closed maqdk closed 10 years ago

maqdk commented 10 years ago

I am trying to load content in a modal via ajax using the Remote option. But cant seem to get it working.

My view looks following:

@Html.Bootstrap().Button().Text("Edit").TriggerModal("editModal").PrependIcon(new Icon("fa fa-pencil")).Style(ButtonStyle.Primary).Size(ButtonSize.Mini)

        @using (var modal = Html.Bootstrap().Begin(new Modal().Id("editModal").Remote("/Admin/ProductPacks/Edit/5")))
        {
            @modal.Header("Modal Header!")

            using (modal.BeginFooter())
            {
                @Html.Bootstrap().Button()
            }
        }

the path /Admin/ProductPacks/Edit/5 returns a partial view.

How can i get it to work?

DmitryEfimenko commented 10 years ago

according Bootstrap docs:

If a remote URL is provided, content will be loaded one time via jQuery's load method and injected into the .modal-content div. ... which means that you need empty .BeginBody() there

maqdk commented 10 years ago

I have added an empty .BeginBody() but it still doesnt work?

    @using (var modal = Html.Bootstrap().Begin(new Modal().Id("editModal").Remote("/Admin/ProductPacks/Edit/5")))
    {
        @modal.Header("Modal Header!")
        using (modal.BeginBody())
        {
        }
        using (modal.BeginFooter())
        {
            @Html.Bootstrap().Button()
        }
    }
DmitryEfimenko commented 10 years ago

verify that a div with class modal-content is rendered. If so, make sure that call to your action takes place. Let me know results

maqdk commented 10 years ago

there is a div with calss modal-body that gets rendered.

i have also tried manually adding a div with class modal-content but still no luck.

maqdk commented 10 years ago

I have also tried adding a breakpoing in my controller where the partialview is being returned. and it looks like the call is not reaching there at all.

DmitryEfimenko commented 10 years ago

see if the call is happening at all... in the network tab of browser developer tools or fiddler... if not, make sure you have all Bootstrap js files referenced correctly...

maqdk commented 10 years ago

I had a URL issue. Now the call is happening just fine. But now when i click on edit button, the modal does not appear, but the screen goes dark as if the modal should have appeared (if you know what i mean). Here is my code and screenshot that also shows the ajax request working fine:

        @*Button*@
        @Html.Bootstrap().Button().Text("Edit").TriggerModal("editModal").PrependIcon(new Icon("fa fa-pencil")).Style(ButtonStyle.Primary).Size(ButtonSize.Mini).Data(new { id = pack.Id })

        @*Modal*@
        @using (var modal = Html.Bootstrap().Begin(new Modal().Id("editModal").Remote("/MvcShop/Admin/ProductPacks/Edit")))
        {
            @modal.Header("Modal Header!")
            using (modal.BeginBody())
            { }
            <div class="modal-content"></div>
            using (modal.BeginFooter())
            {
                @Html.Bootstrap().Button()
            }
        }

image

DmitryEfimenko commented 10 years ago

you probably should not have both:

using (modal.BeginBody())
{ }

and

<div class="modal-content"></div>
maqdk commented 10 years ago

Yea, i have tried them with one each as well and its the same result.

the reason i was testing with both is because modal.beginbody renders class modal-body and not modal-content as you mentioned yesterday that was necessary.

DmitryEfimenko commented 10 years ago

I just rechecked Bootstrap's requirements for Modal. their modal-content div actually wraps modal-body. So I guess you don't need either of these. What does the partial look like that is returned by the remote action?

maqdk commented 10 years ago

i have tried without either of those. still the same result.

partial returns some static text. i have not implemented any logic in there yet. wanted to get the ajax call working first before i work on partial.

DmitryEfimenko commented 10 years ago

perhaps partial needs to return appropriate modal header, body, and footer for modal to display. Give that a try

maqdk commented 10 years ago

do you mean i should move the whole modal into the partial? i tried putting following in partial, and removing it from the details view. but now when i click on edit, nothing happens at all:

@using (var modal = Html.Bootstrap().Begin(new Modal().Id("editModal").Remote("/MvcShop/Admin/ProductPacks/Edit")))
{
    @modal.Header("Modal Header!")

    using (modal.BeginBody())
    { 
        <p>This is edit partial...</p>
    }
    using (modal.BeginFooter())
    {
        @Html.Bootstrap().Button()
    }
}
DmitryEfimenko commented 10 years ago

no, no you need only header, body, and footer there. You'll need to do something similar to what's described here

maqdk commented 10 years ago

Ok, just tried it. Now my detail view looks like following:

        @*Modal popul for editing pack size*@
        @using (var modal = Html.Bootstrap().Begin(new Modal().Id("editModal").Remote("/MvcShop/Admin/ProductPacks/Edit")))
        {

        }

And partial looks like following:

@{

    var modal = Html.Bootstrap().Misc().GetBuilderFor(new Modal().Id("editModal"));
}

@modal.Header("Modal Header!")

@using (modal.BeginBody())
    {
    <p>This is edit partial...</p>
    }
@using (modal.BeginFooter())
    {
    @Html.Bootstrap().Button()
    }

I do get a modal dialog now with static content, but it does not have its background or anything. and the dialog is taking whole screen width. see this screenshot: image

DmitryEfimenko commented 10 years ago

all right, I see what's going on. The answer is here Basically, make sure you are running Bootstrap 3.1, not Bootstrap 3.0

maqdk commented 10 years ago

Well.. that might be the solution. i might just have to implement my own "manual" solution via jquery. was hoping i could use BMVC for this.

Thanks a lot for your help though. i appriciate it.

DmitryEfimenko commented 10 years ago

Manual surely could work, though updating your Bootstrap css/js files should be easy enough. Anyway, good luck on your project.

maqdk commented 10 years ago

I upgraded to 3.1 and it works fine. Now i have one last thing left. How do i read the data attribute of button launching the modal, and pass it to the Remote path? In my detail view i have:

@Html.Bootstrap().Button().Text("Edit").TriggerModal("editModal").PrependIcon(new Icon("fa fa-pencil")).Style(ButtonStyle.Primary).Size(ButtonSize.Mini).Data(new { id = pack.Id })

        @using (var modal = Html.Bootstrap().Begin(new Modal().Id("editModal").Remote("/MvcShop/Admin/ProductPacks/Edit")))
        {

        }

I need to pass the data-id of button to Remote path.

Any help will be appriciated.

Thank you

DmitryEfimenko commented 10 years ago

To my knowledge this can only be done with custom JavaScript

maqdk commented 10 years ago

Now i have got everything working except that when i submit the form in model, the dialog itself disappears but the black overlay background is still there. i cannot get it to dismiss the modal properly. Here is what i have:

Detail View:

                using (r.BeginCell())
                {
                    @Html.Bootstrap().Button().Text("Edit").TriggerModal("editModal" + pack.Id.ToString()).PrependIcon(new Icon("fa fa-pencil")).Style(ButtonStyle.Primary).Size(ButtonSize.Mini)
                    using (var modal = Html.Bootstrap().Begin(new Modal().Id("editModal" + pack.Id.ToString()).Remote(@Url.Action("EditPack", "Products", new { id = pack.Id.ToString()}))))

                    {

                    }
                }

Partial view with modal:

@{

    var modal = Html.Bootstrap().Misc().GetBuilderFor(new Modal().Id("editModal" + Model.Id.ToString()));
}

@using (var f = Ajax.Bootstrap().Begin(new Form(), new AjaxOptions { HttpMethod = "post", InsertionMode = InsertionMode.Replace, UpdateTargetId = "ListOfPacks", OnSuccess = "$('#editModal" + Model.Id.ToString() + "').modal('hide');" }))
{
    @modal.Header("Edit Pack")
    using (modal.BeginBody())
    {
        @Html.HiddenFor(x => x.Id)
        @Html.HiddenFor(x => x.ProductId)
        @Html.HiddenFor(x => x.Special)
        @f.FormGroup().TextBoxFor(x => x.SortOrder)
        @f.FormGroup().TextBoxFor(x => x.Price)
        @f.FormGroup().TextBoxFor(x => x.CostPrice)
        @f.FormGroup().TextBoxFor(x => x.RetailPrice)
        @f.FormGroup().TextBoxFor(x => x.Volume)
        @f.FormGroup().TextBoxFor(x => x.Status)
        @f.FormGroup().TextBoxFor(x => x.SizeId)
        @f.FormGroup().TextBoxFor(x => x.StockNumber).Readonly()

    }
    using (modal.BeginFooter())
    {
        @Html.Bootstrap().SubmitButton().Text("Save")
        @Html.Bootstrap().Button().Text("Close").Data(new { dismiss = "modal" })
    }
}

If i add data-dismiss on submit button, then it only dismisses the modal correctly, but does not submit the form.

Any help will be appreciated.

DmitryEfimenko commented 10 years ago

again, you might need custom javascript to deal with your scenario. Something like this: Change your form to be a regular form, not ajax form. You'll be handing submit via JS.

using (modal.BeginFooter())
{
    @Html.Bootstrap().Button().Id("btn-save").Text("Save")
    @Html.Bootstrap().Button().Text("Close").Data(new { dismiss = "modal" })
}

JS:

$(document).on('click', '#btn-save', function(){
    var form = $('[selector-of-your-form]')
    $.ajax({
        url: form.attr('action'),
        type: 'POST',
        data: form.serialize(),
        success: function(data) {
            // do whatever you want with the result
            $('#editModal' + whateverIsYourId).modal('hide');
        }
    });
});
maqdk commented 10 years ago

Ok, will give it a try.

I did try following ajaxoption on my form. shouldnt that be doing the trick? it doesnt work though:

OnSuccess = "$('#editModal" + Model.Id.ToString() + "').modal('hide');"
DmitryEfimenko commented 10 years ago

you can try this also

OnSuccess = "closeModal"

JS:

function closeModal() {
    var id = '@Model.Id.ToString()'
    $('#editModal' + id).modal('hide');
}