Nate Pet
Nate Pet

Reputation: 46322

MVC Jquery Dialog Box not appearing on POST

I am using MVC C# with Jquery Dialog. When I do a POST, I am redirected to a normal page vs the dialog box.

Here is what my code looks like:

inside View:

    <div id="dialog" title="" style="overflow: hidden;">
    </div>

Within my click event I have the following:

      $('#dialog').dialog({
                   autoOpen: false,
                   width: 500,
                   resizable: true,
                   title: 'PVT Report',
                   modal: true,           
                   buttons: {
                       "Close": function () {
                       $(this).dialog("close");
                        }
                      }
                  });


       $('#dialog').dialog('open');

                 $.ajax({
                           url: url,
                           type: 'GET',
                           success: function(result) {

                           if (result.success) 
                           {
                             $('#dialog').dialog('close');
                          } 
                           else 
                           {
                              $('#dialog').html(result);
                           }
                         }  
                  });
                 }

It goes to the url just fine and displays the dialog box. When I do a POST, it did does not go back to the dialog box but instead goes to a regular page:

Below are my GET and POST:

     public ActionResult FileUpload(int id)
     {
        var model = new FileUpload { PlNum = id}           
        return PartialView(model);
     }


    [HttpPost]
    public ActionResult FileUpload(HttpPostedFileBase file, FileUpload model)
    {
        // Verify that the user selected a file
        if (file != null && file.ContentLength > 0)
        {
            // extract only the fielname
            var fileName = Path.GetFileName(file.FileName);
            string extension = Path.GetExtension(file.FileName);

            if (extension != ".pdf")
            {
                TempData["ErrMessage"] = "Error, wrong file type. File must be a PDF file.";
                return RedirectToAction("FileUpload", new { id = model.PlNum });
            }

.....

Here is my View:

@using (Html.BeginForm("FileUpload", "Plt", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
       @Html.HiddenFor(a => a.PlNum)     
       <p>File upload for Pl# @Model.PlNum</p>
       <input type="file" name="file" />
       <input type="submit" value="OK" />
}

I thought doing it with $.ajax would be fine. I think one of my issue is that when I do a redirecttoAction, the dialog box is no longer open..

Note that in either way, if successful or if not successful, I like to return back to the dialog box. If successful, there will be a message that says "Successful". If not successful, there will be an error message.

Upvotes: 1

Views: 1946

Answers (1)

Darin Dimitrov
Darin Dimitrov

Reputation: 1039588

Just to make sure that I have correctly understood your question, please allow me to first restate your goals before answering. Please correct me if I am wrong.

  1. You want to display some partial view containing a form inside a jQuery UI dialog
  2. This form contains a file upload input which would allow for the user to select and upload a file to the server from his computer
  3. You want this upload to happen asynchronously using AJAX
  4. If the upload succeeds you want to thank the user for having uploaded the file and close the jQuery dialog
  5. If there's an error during the request you want to display an error message to the user. For example if he didn't select any file even if this field was required

If I didn't understand some of your your goals, you could stop reading my answer and update your question to provide more information about what exactly are you trying to achieve.

The challenge here consists in uploading a file with an AJAX request. As you know this is not possible with standard techniques. There are many workarounds though. You could use a plugin such as Uploadify or FineUploader or use the new HTML5 File API which allows you to achieve that and which is supported by all modern browsers (no, IE9 is not a modern browser, sorry). I will cover the lats option because we are in 2013 and we all use modern browsers and nobody gives a ... about IE.

So as in every ASP.NET MVC application we could start by designing a view model which will meet the requirements of our view and the information we would like to have in the upload form:

public class MyViewModel
{
    public int Id { get; set; }

    [Required]
    public HttpPostedFileBase File { get; set; }
}

Then we could have a controller that will handle the process:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Upload(int id)
    {
        var model = new MyViewModel
        {
            Id = id
        };
        return PartialView(model);
    }

    [HttpPost]
    public ActionResult Upload(MyViewModel model)
    {
        if (!ModelState.IsValid)
        {
            // There was a validation error, probably the user didn't select any file
            // we set the status code to 400 (BadRequest) and return the 
            // same partial which will contain the validation error
            Response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest;
            return PartialView(model);
        }

        // at this stage we know that the model is valid => 
        // we could process the uploaded file. In this particular example
        // I am saving the uploaded file to the App_Data folder on the server
        // and ignoring the Id parameter of the view model. Obviously
        // in a more real world application here you might want to store the
        // physical location of this file in your data store as well as it's MIME type
        // so that you could display it later
        var file = Path.Combine(
            Server.MapPath("~/App_Data"), 
            Path.GetFileName(model.File.FileName)
        );
        model.File.SaveAs(file);

        // we have finished => let's set the response status code to 
        // 204 (NoContent) so that the client side javascript that I will show
        // later in my answer could distinguish this case from the error scenario
        Response.StatusCode = (int)System.Net.HttpStatusCode.NoContent;

        // we will return an EmptyResult because in this particular example
        // I don't really care about returning some information to the client
        // from the server. If you care you could of course set the status code 
        // to 200 (Success) and return, say, a JsonResult here
        return new EmptyResult();
    }
}

Not much to be said about this controller. Pretty standard stuff. An Index action to serve some dummy view that will contain a link to initiate the upload process. An Upload (GET) action to serve the upload form partial and of course an Upload (POST) action to handle the actual file upload (in this oversimplified example I am storing the file on the server).

Then we could have the corresponding ~/Views/Home/Index.cshtml dummy view:

@model MyViewModel
@Html.ActionLink(
    "click here to upload a file", 
    "Upload", 
    new { id = 154 }, 
    new { id = "uploadLink" }
)
<div id="dialog"></div>

Easy: a strongly typed view containing an anchor that will subsequently (see later in my answer) be AJAXified in order to show the upload partial inside a jQuery dialog and a div placeholder for the dialog.

Next we could write the upload partial that will contain the form (~/Views/Home/Upload.cshtml):

@model MyViewModel

@using (Html.BeginForm(null, null, new { id = Model.Id }, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    <div>
        @Html.LabelFor(x => x.File)
        @Html.TextBoxFor(x => x.File, new { type = "file" })
        @Html.ValidationMessageFor(x => x.File)
    </div>
    <button type="submit">Upload</button>
}

Once again, pretty standard stuff here => an HTML form containing a file input that will allow the user to select a file to be uploaded.

And the final step of course is the javascript that will make all this come alive. You could put this javascript in a Scripts section inside your Index view that you overrode from the _Layout. Ideally scripts should be placed at the end of the document, before the closing </body> tag (that's the reason why I am not wrapping the .click() handler subscription in a document.ready):

$('#uploadLink').click(function () {
    $.ajax({
        url: this.href,
        type: 'GET',
        cache: false,
        success: function (result) {
            $('#dialog').html(result).dialog({
                autoOpen: true,
                width: 500,
                resizable: true,
                title: 'PVT Report',
                modal: true,
                buttons: {
                    'Close': function () {
                        $(this).dialog("close");
                    }
                }
            });
        }
    });
    return false;
});

$('#dialog').on('submit', 'form', function () {
    var xhr = new XMLHttpRequest();
    xhr.open(this.method, this.action);
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
            if (xhr.status == 204) {
                // upload was successful => thank the user and close
                // the dialog
                alert('Thank you for uploading the file');
                $('#dialog').dialog('close');
            } else if (xhr.status == 400) {
                // validation error occurred on the server => redisplay the form
                $('#dialog').html(xhr.responseText);
            }
        }
    };
    xhr.send(new FormData(this));
    return false;
});

2 events are handled by this javascript: the click on the anchor in the main view that shows the jQuery dialog and the form submission that will upload the file using an AJAX request and the HTML 5 File API.

Upvotes: 1

Related Questions