views:

319

answers:

2

Hi All,

I am attempting to create a form where a user is able to enter your typical form values textboxes etc, but also upload a file as part of the form submission. This is my View code it can be seen that the File upload is identified by the MCF id:

<% using (Html.BeginForm("Create", "Problem", FormMethod.Post, new { id = "ProblemForm", enctype = "multipart/form-data" }))
   {%>

        <p>
            <label for="StudentEmail">Student Email (*)</label>
            <br />
            <%= Html.TextBox("StudentEmail", Model.Problem.StudentEmail, new { size = "30", maxlength=26 })%>
            <%= Html.ValidationMessage("StudentEmail", "*") %>
        </p>
        <p>
            <label for="Type">Communication Type (*)</label>
            <br />
            <%= Html.DropDownList("Type") %>
            <%= Html.ValidationMessage("Type", "*") %>
        </p>
        <p>
            <label for="ProblemDateTime">Problem Date (*)</label>
            <br />
            <%= Html.TextBox("ProblemDateTime", String.Format("{0:d}", Model.Problem.ProblemDateTime), new { maxlength = 10 })%>
            <%= Html.ValidationMessage("ProblemDateTime", "*") %>
        </p>
        <p>
            <label for="ProblemCategory">Problem Category (* OR Problem Outline)</label>
            <br />
            <%= Html.DropDownList("ProblemCategory", null, "Please Select...")%>
            <%= Html.ValidationMessage("ProblemCategory", "*")%>
        </p>
        <p>
            <label for="ProblemOutline">Problem Outline (* OR Problem Category)</label>
            <br />
            <%= Html.TextArea("ProblemOutline", Model.Problem.ProblemOutline, 6, 75, new { maxlength = 255 })%>
            <%= Html.ValidationMessage("ProblemOutline", "*") %>
        </p>
        <p>
            <label for="MCF">Mitigating Circumstance Form</label>
            <br />
            <input id="MCF" type="file" />
            <%= Html.ValidationMessage("MCF", "*") %>
        </p>
        <p>
            <label for="MCL">Mitigating Circumstance Level</label>
            <br />
            <%= Html.DropDownList("MCL") %>
            <%= Html.ValidationMessage("MCL", "*") %>
        </p>
        <p>
            <label for="AbsentFrom">Date Absent From</label>
            <br />
            <%= Html.TextBox("AbsentFrom", String.Format("{0:d}", Model.Problem.AbsentFrom), new { maxlength = 10 })%>
            <%= Html.ValidationMessage("AbsentFrom", "*") %>
        </p>
        <p>
            <label for="AbsentUntil">Date Absent Until</label>
            <br />
            <%= Html.TextBox("AbsentUntil", String.Format("{0:d}", Model.Problem.AbsentUntil), new { maxlength = 10 })%>
            <%= Html.ValidationMessage("AbsentUntil", "*") %>
        </p>
        <p>
            <label for="AssessmentID">Assessment Extension</label>
            <br />
            <%= Html.DropDownList("AssessmentID") %>
            <%= Html.ValidationMessage("AssessmentID", "*") %>

            <%= Html.TextBox("DateUntil", String.Format("{0:d}", Model.AssessmentExtension.DateUntil), new { maxlength = 16 })%>
            <%= Html.ValidationMessage("DateUntil", "*") %>
        </p>
        <p>
            <label for="Details">Assessment Extension Details</label>
            <br />
            <%= Html.TextArea("Details", Model.AssessmentExtension.Details, 6, 75, new { maxlength = 255 })%>
            <%= Html.ValidationMessage("Details", "*") %>
        </p>
        <p>
            <label for="RequestedFollowUp">Requested Follow Up</label>
            <br />
            <%= Html.TextBox("RequestedFollowUp", String.Format("{0:d}", Model.Problem.RequestedFollowUp), new { maxlength = 16 })%>
            <%= Html.ValidationMessage("RequestedFollowUp", "*") %>
        </p>
        <p>
            <label for="StaffEmail">Staff</label>
            <br />
            <%= Html.ListBox("StaffEmail", Model.StaffEmail, new { @class = "multiselect" })%>
            <%= Html.ValidationMessage("StaffEmail", "*")%>
        </p>
        <p>
            <input class="button" type="submit" value="Create Problem" />
        </p>

This is my controller code:

        [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create(Problem problem, AssessmentExtension assessmentExtension, Staff staffMember, HttpPostedFileBase file, string[] StaffEmail)
    {
        if (ModelState.IsValid)
        {
            try
            {

                Student student = studentRepository.GetStudent(problem.StudentEmail);
                Staff currentUserStaffMember = staffRepository.GetStaffWindowsLogon(User.Identity.Name);

                var fileName = Path.Combine(Request.MapPath("~/App_Data"), Path.GetFileName(file.FileName));
                file.SaveAs(@"C:\Temp\" + fileName);

                if (problem.RequestedFollowUp.HasValue)
                {
                    String meetingName = student.FirstName + " " + student.LastName + " " + "Mitigating Circumstance Meeting";
                    OutlookAppointment outlookAppointment = new OutlookAppointment(currentUserStaffMember.Email, meetingName, (DateTime)problem.RequestedFollowUp, (DateTime)problem.RequestedFollowUp.Value.AddMinutes(30));
                }

                problemRepository.Add(problem);
                problemRepository.Save();

                if (assessmentExtension.DateUntil != null)
                {
                    assessmentExtension.ProblemID = problem.ProblemID;
                    assessmentExtensionRepository.Add(assessmentExtension);
                    assessmentExtensionRepository.Save();
                }

                ProblemPrivacy problemPrivacy = new ProblemPrivacy();
                problemPrivacy.ProblemID = problem.ProblemID;
                problemPrivacy.StaffEmail = currentUserStaffMember.Email;
                problemPrivacyRepository.Add(problemPrivacy);

                if (StaffEmail != null)
                {
                    for (int i = 0; i < StaffEmail.Length; i++)
                    {
                        ProblemPrivacy probPrivacy = new ProblemPrivacy();
                        probPrivacy.ProblemID = problem.ProblemID;
                        probPrivacy.StaffEmail = StaffEmail[i];
                        problemPrivacyRepository.Add(probPrivacy);
                    }
                }

                problemPrivacyRepository.Save();

                return RedirectToAction("Details", "Student", new { id = student.Email });

            }
            catch
            {
                ModelState.AddRuleViolations(problem.GetRuleViolations());
            }
        }

        return View(new ProblemFormViewModel(problem, assessmentExtension, staffMember));
    }

This form was working correctly before I had to switch to using a non-AJAX file upload, this was due to an issue with Flash when enabling Windows Authentication which I need to use.

It appears that when I submit the form the file is not sent and I am unsure as to why? I have also been unsuccessful in finding an example online where a file upload is used in conjunction with other input types.

Another query I have is that for Create, and Edit operations I have used a PartialView for my forms to make my application have higher code reuse. The form action is normally generated by just using:

Html.BeginForm()

And this populates the action depending on which Url is being used Edit or Create. However when populating HTML attributes you have to provide a action and controller value to pass HTML attributes.

using (Html.BeginForm("Create", "Problem", FormMethod.Post, new { id = "ProblemForm", enctype = "multipart/form-data" }))

Is it possible to somehow populate the action and controller value depending on the URL to maintain code reuse? Thinking about it whilst typing this I could set two values in the original controller action request view data and then just populate the value using the viewdata values?

Any help on these two issues would be appreciated, I'm new to asp.net mvc :-)

Thanks,

Jon

ANSWER

Ok guys worked out the issue and its incredibly simple I didn't have the HTML name attribute on the file component of my form:

<input id="MCF" name="MCF" type="file" />

Now this binds to my method signature!

A: 

With the first issue, it looks like your action method signature is wrong. Because your fileInput has an ID of MCF, the HttpPostedFileBase parameter should have the same name so that the model binder knows to bind to that action method parameter.

E.g.

public ActionResult Create(Problem problem, AssessmentExtension assessmentExtension, Staff staffMember, HttpPostedFileBase mcf, string[] StaffEmail)

As for the second issue... you could try something like this:

<form method="post" id="ProblemForm" action="<%= Url.Action(ViewContext.RouteData.Values["action"].ToString()) %>" enctype="multipart/form-data">

The current controller will also be in RouteData.Values but if you're after the area, that'll be in RouteData.DataTokens.

HTHs,
Charles

Charlino
I tried both of your suggestions, with the File one I changed the name and it is still set to null when I submit the form.As for the second issue I am trying to use the HTML helper methods, your method would most defiantly work the way you have suggested as I saw it printed out the controller/action. However whilst trying this I found the resolution of Html.BeginForm(null, null, FormMethod.Post, new { id = "ProblemForm", enctype = "multipart/form-data" })) by passing null it seems to default the controller and action according to the URL.
Jonathan Stowell
Good to hear you fixed the second one. As for the first issue, maybe try putting a debug break point in your controller and have a look at the Controller.Request.Files object - is there a file in the request? If so, what is it's key? If there isn't a file then the problem exists before hitting the model binder. Let me know.
Charlino
There's no file in Request.Files count = 0. So could this have something to do with the ViewModel I'm using as the ProblemForm is constructed from a couple objects. I added a HttpPostedFileBase attribute to that class but I'll post up the code.
Jonathan Stowell
A: 

Ok guys worked out the issue and its incredibly simple I didn't have the HTML name attribute on the file component of my form:

<input id="MCFile" name="MCFile" type="file" />

I have changed my method signature to match the name:

public ActionResult Create(Problem problem, AssessmentExtension assessmentExtension, Staff staffMember, HttpPostedFileBase MCFFile, string[] StaffEmail)

Now this binds to my method signature!

Jonathan Stowell