Search code examples
c#jqueryasp.net-mvcrazorasp.net-mvc-4

Mvc 4 dynamic form file upload


I'm building a site where users can fill in a multi-page form which is dynamically generated from a database. I use JQuery to post the data to my controller and return the next page of the form. It works fine for everything except files.

The problem is getting files to be posted to my controller, I'm using the HtmlHelpers from this post to generate the html for file fields.

My models:

public class QuestionPage
{
    public const string Next = "next";
    public const string Prev = "prev";
    public const string Save = "save";

    public int currentID { get; set; }
    public bool advanced { get; set; }
    public QuestionPageItem[] questions { get; set; }
    public int pagenumber { get; set; }
    public int? next {  get; set; }
    public int? prev { get; set; }

    public string submitaction { get; set; }
    public int? gotoID { get; set; }

    public Dictionary<Int32, QuestionPageTitle> titles { get; set; }
}

public class QuestionPageItem
{
    public int id { get; set; }
    public string question { get; set; }
    public string description { get; set; }
    public bool required { get; set; }
    public QuestionType type { get; set; }
    public object answer { get; set; }
    public int? enableID { get; set; }
    public bool initHidden { get; set; }
    public QuestionOptionIndexModel[] options { get; set; }
}

My static view:

using (Html.BeginForm("Form", "QuestionPage", new { id = Model }, FormMethod.Post, new { id = "frm" + Model, name = Model, enctype = "multipart/form-data" }))
{
    <div id="formcontainer">
        @Html.Partial("_FormPartial", Model)
    </div>
} 

My partial view (_FormPartial) which gets replaced by jQuery ajax, simplified:

    @model DPDF.Models.QuestionPage
    Some hidden fields...
    @for (int i = 0; i < Model.questions.Length; i++)
    {
        var question = Model.questions[i];
        Some hidden fields...
        <tr class="@(question.initHidden ? "hidden" : "")" id="@(question.id)" >
            show textbox or textarea or radio etc depending on question type, for instance
            @Html.TextBox("questions[" + i + "].answer", (question.answer == null ? string.Empty : question.answer.ToString()))
            in case of file field
            @Html.FileBox("questions[" + i + "].answer")
        </tr>
    }

The data gets bound to the answer field in the question object. My controller action:

    [AllowAnonymous]
    public ActionResult Form(QuestionPage model)
    {

        if (!ModelState.IsValid)
            return View(model);

        // this is to make some hidden fields get the correct new values
        ModelState.Clear();

        // do stuff to determine what page to show next...
        // save data, extract value from model.answer object depending on question type
        // make new model and return it
        if (Request.IsAjaxRequest())
            return PartialView("_FormPartial", model);
        return View(model);
    }

Normal fields like textboxes return something like String[]{"test"}, file fields return null.

EDIT: Perhaps the problem lies with javascript? This is the code that posts to the server:

    var $form = $(form);
    var $container = $('#formcontainer');
    $container.hide('slide', { direction: direction }, 500, function () {
        $.ajax({
            url: $form.attr('action'),
            type: $form.attr('method'),
            data: $form.serialize(),
            success: function (data, textStatus, jqXHR) {
                $container.html(data);
                updateOverzicht();
                $container.show('slide', { direction: (direction == 'left') ? 'right' : 'left' }, 500);
            }
        });
    });

Solution

  • Change the type of answer inside the QuestionPageItem class to be of HttpPostedFiledBase not object:

    public HttpPostedFileBase answer { get; set; }
    

    ACTUALLY: His real problem is because he was trying to submit his form as part of an Ajax request in jQuery.