8

I have a complex view model that I am passing to a create view. When I enter data on the page and post it the model is empty. Both the fields in the sub-object and the "test" field are empty. Why?

public class ContactIncident
{
    [Key]
    public int Id { get; set; }

    [DataType(DataType.MultilineText)]
    public string Description { get; set; }

    [Display(Name = "Incident Date")]
    [DataType(DataType.Date)]
    public DateTime? IncidentDateTime { get; set; }

    [Display(Name = "Follow Up Date")]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
    [DataType(DataType.Date)]
    public DateTime? FollowUpDate { get; set; }
}

public class IncidentManager
{    
    public ContactIncident Incident { get; set; }
    public string Test { get; set; }
}


public ActionResult Create(int? id)
{
    IncidentManager im = new IncidentManager();
    ContactIncident ci = new ContactIncident();
    ci.IncidentDateTime = DateTime.Now;
    ci.FollowUpDate = DateTime.Now.AddDays(14);
    im.Incident = ci;
    return View(im);
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(IncidentManager im)
{
    if (ModelState.IsValid)
    {
        ContactIncident ci = new ContactIncident();
        ci.IncidentDateTime = incident.Incident.IncidentDateTime;
        ci.Description = im.Incident.Description;
        return RedirectToAction("Index");
    }
    return View(incident);
}

View:

@model MyApp.Web.ViewModels.IncidentManager
@{
    ViewBag.Title = "Edit Incident";
}
<h4>@ViewBag.Title</h4>    
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    <div class="form-horizontal well">
        @Html.ValidationSummary(true)      
        @Html.EditorFor(model=>model.Test)   
        <div class="row">
            <div class="col-md-2">
                @Html.LabelFor(model => model.Incident.IncidentDateTime)
            </div>
            <div class="col-md-2">
                @Html.DisplayFor(model => model.Incident.IncidentDateTime)
            </div>
        </div>
        <div class="row">
            <div class="col-md-2">
                @Html.LabelFor(model => model.Incident.Description)
            </div>
            <div class="col-md-10">
                @Html.EditorFor(model => model.Incident.Description, new { htmlAttributes = new { @class = "form-control", rows = "5" }, })
            </div>
                   <div class="col-md-2">
                @Html.LabelFor(model => model.Incident.FollowUpDate)
            </div>
            <div class="col-md-2">
                @Html.EditorFor(model => model.Incident.FollowUpDate, new { htmlAttributes = new { @class = "form-control"}, })
            </div>
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Save" class="btn btn-default" />
        </div>
    </div>
}
2
  • 2
    can u show your cshtml? wanna see how you are defining the form elements Commented Jul 24, 2014 at 16:26
  • 1
    I assume ci.IncidentDateTime = incident.Incident.IncidentDateTime; is a typo and its really ci.IncidentDateTime = im.Incident.IncidentDateTime;. Ditto for return View(incident); being return View(im); (your code as is does not compile). Otherwise your code works fine although IncidentDateTime will be null because you do not create a form input for it. Commented Sep 30, 2017 at 22:30

1 Answer 1

9

The problem is that the DefaultModelBinder won't be able to map nested models properly if you use a different parameter name. You must use the same parameter name as the ViewModel name.

public ActionResult Create(IncidentManager incidentManager)

As a general practice, always use the name of the model as the parameter name to avoid mapping problems.

UPDATE:

The DefaultModelBinder uses "convention based" mapping.

IncidentManager.Incident = incidentManager.Incident (will map)
IncidentManager.Incident = im.Incident    //won't map because 'im' != 'incidentManager'
Sign up to request clarification or add additional context in comments.

7 Comments

Please elaborate. Are you speaking to the class IncidentManager? If so why?
I made the change and it worked. I've run into this several times before and always worked around it, but this will be much better.
You must use the same parameter name as the ViewModel name. is WRONG!. The parameter name can be any valid name, so long as it not the same name as one of the properties in the model.
@StephenMuecke - They may have updated the model binder logic since this post. Since this is the accepted answer, this must have fixed the posters problem. Regardless of your opinion.
No, it has no changed (its the same for MVC-3, MVC-4 and MVC-5 with respect to this)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.