I have the following POCO classes:
public class Location
{
public int LocationId { get; set; }
public string Name { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public string Country { get; set; }
public float? Latitude { get; set; }
public float? Longitude { get; set; }
public string PhoneNumber { get; set; }
public string EmailAddress { get; set; }
public string Website { get; set; }
public virtual ICollection<Program> Programs { get; set; }
public virtual ICollection<PlayerType> PlayerTypes { get; set; }
}
public class PlayerType
{
public int PlayerTypeId { get; set; }
public string Name { get; set; }
public int SortOrder { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<Location> Locations { get; set; }
}
And a View Model Class
public class LocationViewModel
{
public Location Location { get; set; }
public IList<PlayerType> SelectPlayerTypes { get; set; }
public LocationViewModel()
{
Location = new Location();
}
}
Within my Create Form, I have defined the model as
@model Locator.Models.LocationViewModel
And have fields like the following:
div class="editor-label">
@Html.LabelFor(model => model.Location.Name)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Location.Name)
@Html.ValidationMessageFor(model => model.Location.Name)
</div>
In my controller to handle the POST I have
[HttpPost]
public ActionResult Create(LocationViewModel location)
{
if (ModelState.IsValid) {
locationRepository.InsertOrUpdate(location.Location);
locationRepository.Save();
return RedirectToAction("Index");
}
location.SelectPlayerTypes = golferTypeRepository.All.Where(p => p.IsActive).ToList();
return View(location);
}
The problem is that I have a Location object but none of the properties are to set to the values entered in the form.
Am I doing something wrong here?
Thanks
Let me add here yeat another reason why the model binder would not work properly.
I had a model with the property
ContactPhone
, somewhere along the way I decided to change the name of this property toPhone
, then all of a sudden model binding for this property stopped working when I was trying to create a new instance.The problem was on the
Create
action in my controller. I have used the default Visual Studio scaffolding and it has created the method signature as this:Pay attention to the
Bind
attribute, the scaffolder created the fields using the original nameContactPhone
and as this is a string, it was not refactored. As the new fieldPhone
was not being included, it's value was ignored by the model binder.I hope this saves someone's time.
Good luck!
Here's the problem:
Do you see it? It's the name of your action argument:
location
.Look at your view model now, it has a property named
Location
:This confuses the model binder. It no longer knows whether you need to bind the
LocationViewModel
or its property.So simply rename to avoid the conflict:
And another reason: Normally I use the built in editors or displays for and would have never encountered this issue. However in this case I required a semi-custom control. Basically a drop down with lots of data attributes on the options.
What I was doing, for my select tag was:
Now everything was posting back and to the untrained eye it looked like everything should work and bind. However the IdFor helper renders sub models with an underscore. The model binder doesn't interpret underscores as a class hierarchy indicator. What should be separating them is a dot. Which comes from NameFor:
NameFor fixed all my issues.
Just to reiterate what Tod was saying, the model binder needs a 'name' attribute on the HTML element to map the properties. I was doing a quick test form by hand and only used the 'id' attribute to identify my elements.
Everything fell into place when I added the 'name' attribute.