6

What I need to do is to show a popup to add a new record to my database, im using bootstrap 3 and I love it because im not using a single line of jquery, and I have really nice form (obvioulsy they are based in jquery).

I am validating my form via ajax, but the problem now is that my modal never closes, when I try to redirect to an Action the action is loaded inside my modal, so my question is how do I stop my modal?

This is an example of what this code does:

My form:

enter image description here

My form when when validated:

enter image description here

this is perfect with this code:

<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
    <div class="modal-content">
        <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
            <h4 class="modal-title" id="myModalLabel">Add Car</h4>
        </div>
        <div class="modal-body">
            @using (Ajax.BeginForm("ModalAdd", new AjaxOptions() {UpdateTargetId = "mymodalform"}))
            {
                <div id="mymodalform">
                    @Html.Partial("CreatePartialView", new Car())
                </div>
            }
        </div>
    </div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->

and my partial:

@model ControliBootstrap.Models.Car

    <div class="form-horizontal" >
    @Html.ValidationSummary(true)

    <div class="form-group">
        @Html.LabelFor(model => model.Model, new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Model)
            @Html.ValidationMessageFor(model => model.Model)
        </div>
    </div>

    <!--More fields-->

    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Create" class="btn btn-default"/>
        </div>
    </div>
</div>

the problem now is than when model is valid in my controller I go to Index Action which is loaded inside my modal so my question again is how do I close my modal?

enter image description here

here is my controller:

public ActionResult ModalAdd(Car car)
    {
        if (ModelState.IsValid)
        {
            db.Cars.Add(car);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return PartialView("CreatePartialView",car);
    }
bto.rdz
  • 6,636
  • 4
  • 35
  • 52

4 Answers4

15

Just for the record, I found my answer hope it helps someone else, it is really hard to find a full article of this.

I had to use more of jquery but It is a clean answer (I think).

Using data annotations in my model:

[Required]
public string Name { get; set; }

[Required]
public string Phone { get; set; }

Then I created a partial in my shared folder that contains my modal form, so I can make it global.

@model Controli.Models.Provider

<!-- Modal -->
<div class="modal fade" id="mdlnewprovider" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
        @using (Html.BeginForm("Add", "Providers", FormMethod.Post, new { id = "frmnewprovider" }))
        {
        <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
            <h4 class="modal-title" id="myModalLabel">Nuevo Proveedor</h4>
        </div>
        <div class="modal-body">
            <div class="form-group">
                @Html.TextBoxFor(u => u.Name, new { @class = "form-control", @placeholder = HttpUtility.HtmlDecode(@Html.DisplayNameFor(u => u.Name).ToHtmlString()) })
                @Html.ValidationMessageFor(u => u.Name)
            </div>
            <!--More Textboxes and Validaton Messages-->
        </div>
        <div class="modal-footer">
            <input type="submit" value="Agregar" class="btn btn-primary" />
        </div>
        }
        </div><!-- /.modal-content -->
    </div><!-- /.modal-dialog -->
</div><!-- /.modal -->

And the script:

var frmnewprovider = $("#forms-providers-new");
$(document).ready(function () {
frmnewprovider.submit(function (e) {
    e.preventDefault();
    frmnewprovider.validate();
    if (frmnewprovider.valid()) {
        $.ajax({
            url: "/Providers/Add",
            type: "POST",
            contentType: "application/json; charset=utf-8",
            data: JSON.stringify({
                Name: frmnewprovider.find('#Name').val(),
                Phone: frmnewprovider.find('#Phone').val(),
                Email: frmnewprovider.find('#Email').val(),
                Country: frmnewprovider.find('#Country').val(),
                State: frmnewprovider.find('#State').val(),
                Address: frmnewprovider.find('#Address').val()
            }),
            success: function (result) {
                //if record was added to db, then...
                window.location.replace('/Providers/Index'); //we can redirect
                //or simply close our modal.
                //$('#mdlnewprovider').modal('hide');
            },
            error: function(result) {
                alert('error');
            }
        });
    }
});
});

Now all I have to do to render my form where ever I need it is to add these lines:

<button class="btn btn-primary" data-toggle="modal" data-target="#mdlnewprovider">
    Nuevo
</button>

@Html.Partial("Modals/Providers/FrmNew", new Provider())

@section scripts
{
<script src="~/Scripts/Modals/Providers/NewProvider.js"></script>
<!--Where this script is the one above-->
}

Finally since my model was client-side validated I just add my model to my database, and redirect to Index view, while the ajax call hides active modal Update: I recommend to decide if redirect or hide modal at ajax call. like commented.

    public ActionResult Add(Provider provider)
    {
        if (ModelState.IsValid) //Validate in server side too!
        {
            db.Providers.Add(provider);
            db.SaveChanges();
            return Json(new{ success = true}); //return a dummy json, you can pass what
                                               //ever parameter you need. if code reach
                                               //this point. it will always hit success 
                                               //in our ajax call
        }
    }

make sure your web.config contains:

<appSettings>
  <add key="ClientValidationEnabled" value="true" />
  <add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>

Im using these scripts too:

<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>

Please let me know if something could be better.

bto.rdz
  • 6,636
  • 4
  • 35
  • 52
  • 5
    "since my model was client-side validated I just add my model to my database" **NO NO NO NO NO!** Client side validation is to create a better user experience. It guarantees absolutely nothing in terms of security or that the data posted is *actually* valid and limited to what you expect (ex: just a Provider, not including chains of objects that Provider properties point to). You absolutely must still implement server side validation otherwise horrible things will happen. – Robert Levy Jan 09 '14 at 14:28
  • Thanks for your answer @bto.rdz . I am wondering if you could label your code snippets to show where the code goes. Also, a couple more questions: **1.** Where did you put your javascript? In the partial? **2.** I am using validation on my fields. How can I prevent the modal to POST or close while they're still validation issues? Thanks. – megamaiku Apr 06 '17 at 23:32
  • this line did the trick: //$('#mdlnewprovider').modal('hide'); – CountLessQ Nov 02 '20 at 19:09
5

You are going to have to write some jquery - sorry

@using (Ajax.BeginForm("ModalAdd", new AjaxOptions() {UpdateTargetId = "mymodalform", OnSuccess= "$('#myModal').modal('hide');"}))

But its just one line

Greg Ennis
  • 14,917
  • 2
  • 69
  • 74
  • 2
    As far as I know this will not work because returning the partial, although a "failure" according to us, is actually a 200 OK response and therefore will cause the modal to hide because it will trigger the `OnSuccess`. – Rowan Freeman Dec 31 '13 at 03:47
  • Not working, when form is not filled properly my modal closes, when it is correct it load index view in my modal. Is there another way to achive this maybe with more jquery? – bto.rdz Dec 31 '13 at 15:38
  • This worked for me albeit I wasn't using partials...the small jQuery snippet in the AjaxOptions closed my input form and my controller re-opened my results form. – JasonInVegas Dec 04 '17 at 19:50
3

This is a pretty common fundamental problem with this approach.

What we have is an AJAX form that, if the form fails to process, then the partial is re-rendered and returned to the browser so that the original form can be replaced with the new one. If the form succeeds, you want the browser to redirect to a new page but the problem is that the browser is expecting a page returned that it can insert into the modal.

The problem is that the approach is a dichotomy. That is, you need to either do a full page refresh or an AJAX request, but not both in which the appropriate logic is determined by the server.

One solution to this problem:

One thing that I can think of is to change the server's response to a Bad Request (400) when you are returning the partial to indicate that, although you are returning some HTML, the process did not complete successfully.

This means that you can take advantage of the OnFailure AjaxOption. If the result is a failure, then take the response data and insert it as normal in to the modal.

However if the status is 200 OK then return a URL and take advantage of OnSuccess and perform a simple javascript redirect to the URL.

Community
  • 1
  • 1
Rowan Freeman
  • 15,724
  • 11
  • 69
  • 100
  • 2
    I understand your point, and sounds good, the problem is that I dont know how to traslate your solution to code – bto.rdz Dec 31 '13 at 15:00
1

I used JavaScript to control the page redirection and reloading.

Options which I used are:

  1. Return a JavaScript function to refresh the host page instead of a RedirectToAction, when the controller action is successful.

    return this.Content("< script >location.reload()< /script >");

  2. Return a JavaScript function to redirect the client to the page.
    How to redirect to another webpage in JavaScript/jQuery?

  3. RedirectToAction which has the required JavaScript redirection or reloading logic within the view

Community
  • 1
  • 1
MrCheese
  • 3,272
  • 1
  • 12
  • 11