Binding collections in MVC - asp.net-mvc

I have a View Model that consists of an Applicant object and a TeamMember collection. When I post the model back the Team collection is always null. I've tried changing the collection from my original IEnumarable to a List but that didn't make a difference. So I changed the Controllers Edit Action to accept the FormCollection, and verified there was data in viewModel["member.FirstName"]. I'm lost as to why the binding isn't working. I tried to clean out my code samples as much as possible but I'm confused at what I'm missing. Any help is greatly appreciated!
View Model Properties
public class MyViewModel
{
public Applicant ApplicantInfo { get; set; }
public List<TeamMember> TeamMembers { get; set; }
}
Controller
[HttpPost]
public ActionResult Edit(MyViewModel viewModel)
{
// viewModel.ApplicantInfo has the form data
// viewModel.TeamMembers = null
}
View
<% using (Html.BeginForm())
{%>
<h3>
Applicant Information
</h3>
<label>
City
<%: Html.TextBoxFor(m => Model.ApplicantInfo.City)%>
</label>
<label>
State
<%: Html.TextBoxFor(m => Model.ApplicantInfo.State)%>
</label>
<h3>
Team
</h3>
<div>
<% foreach (var member in Model.TeamMembers)
{ %>
<div class="editor-field">
<%: Html.DropDownList("member.Type", Model.GetMemberTypes(member.MemberType.TypeId))%>
</div>
<div class="editor-field">
<%: Html.EditorFor(m => member.FirstName)%>
</div>
<div class="editor-field">
<%: Html.EditorFor(m => member.LastName)%>
</div>
<div class="editor-field">
<%: Html.EditorFor(m => member.Title)%>
</div>
<%} %>
</div>
<p>
<input type="submit" value="Save" />
</p>
<% } %>

I believe that input tags associated with items in a collection (when the model itself is not a collection) need to have an index in the name attribute before you can bind posted data to a view model. Here is the way I usually accomplish this...
<% for (int i=0; i<Model.TeamMembers.Count; i++) { %>
<div class="editor-field">
<%: Html.EditorFor(m => m.TeamMembers[i].FirstName)%>
</div>
<div class="editor-field">
<%: Html.EditorFor(m => m.TeamMembers[i].LastName)%>
</div>
<% } %>
I've also used the template as suggested by Shea, but I have a tad more code trying to force it to render brackets/indexes.
<% foreach (var member in Model.TeamMembers) { %>
<%: Html.EditorFor(x =>
member,
"TeamMember",
"TeamMembers["+(member.Number-1)+"]",
new { MemberTypes = Model.GetMemberTypes(member.MemberType.TypeId) })%>
<% } %>
Here is an old but still relevant article from Phil Haack on the topic.

Try using this:
<%: Html.EditorFor(m => m.TeamMembers) %>
Then, make a shared editor template with a model type of TeamMember. MVC should handle binding everything back to your viewmodel on post for you.

Related

Change method call order when form is posted in MVC

a bit of a strange one here. I've got a blog that's another 'dip my toe into MVC' project that will highlight the benefits of our new Intranet. I have a typical blog post page with the article, comments and a form to submit your comments against a given article.
I have a stringly typed main View ("Post") which has the article content, then two Html.RenderAction methods; one to get the comments and one to add comments via a form.
My issue is that when a comment is posted, my GetComments method is called before AddComment, so when the page is refreshed the new comment isn't visible although it has been added to the database. A quick F5 confirms this. I understand that GetComments is being called first due to it being declared first in the view but I'm not sure how to tell the view to do an add before a get.
Here's my code:
Controller:
public ActionResult AddComment()
{
return PartialView("AddComment");
}
[HttpPost]
public ActionResult AddComment(Comment comment)
{
comment.DateSubmitted = DateTime.Now;
db.Comments.Add(comment);
db.SaveChanges();
return PartialView(comment);
}
public ActionResult GetComments(int articleid)
{
var comments = db.Comments.Where(c => c.ArticleID == articleid).ToList();
return PartialView(comments);
}
Post view
#model IntranetBlog.Models.Article
#{
ViewBag.Title = "Post";
}
<div class="row">
<div class="span12">
<h3>#Html.DisplayFor(modelItem => Model.Title)</h3>
<small>by Ruth Barlow on #Html.DisplayFor(modelItem => Model.DateCreated)</small>
#if (Model.Image != null)
{
<p>
<img src="#Url.Action("GetImage", "Home", new { articleID = Model.ArticleID })" alt="" width="150" height="150" />
</p>
}
<div>
#Html.DisplayFor(modelItem => Model.Body)
</div>
<small>Posted under #Html.DisplayFor(modelItem => Model.Category.Name)</small>
</div>
<div class="span12">
#{
Html.RenderAction("GetComments", "Home", new { articleID = Model.ArticleID });
}
</div>
<div class="span12">
#{
Html.RenderAction("AddComment", "Home", new { articleID = Model.ArticleID });
}
</div>
</div>
GetComments partial:
#model IEnumerable<IntranetBlog.Models.Comment>
#if (Model.Any())
{
<h3>What you're saying</h3>
foreach (var item in Model)
{
<div>
Comment: #Html.DisplayFor(modelItem => item.Body)
</div>
<div>
Submitted by: #Html.DisplayFor(modelItem => item.SubmittedBy)
on #Html.DisplayFor(modelItem => item.DateSubmitted)
</div>
<hr />
}
}
else
{
<p>There are no comments for this post. Why not add one?</p>
}
AddComment partial
#model IntranetBlog.Models.Comment
#using (Html.BeginForm())
{
<h3>Why not leave us a comment?</h3>
#Html.ValidationSummary()
<fieldset>
<div class="editor-label">
#Html.LabelFor(model => model.Body)
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.Body, 20, 20, null)
#Html.ValidationMessageFor(model => model.Body)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SubmittedBy)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SubmittedBy)
#Html.ValidationMessageFor(model => model.SubmittedBy)
</div>
<p>
<input type="submit" value="Add comment" id="AddComment" class="btn btn- primary" />
</p>
</fieldset>
}
Hope this makes sense.
Thanks in advance.
The trick is to use Html.Action instead of Html.RenderAction this will allow you to store the result in a variable and then add it to the display where it is needed.
This will allow you to generate the PartialView in the logical order you need, while displaying them in an other order.
See this post for a quick example on how to do it : https://stackoverflow.com/a/13301057/971693
I would suggest to you a slightly different approach. Instead of using Html.BeginForm, think about using Ajax.BeginForm to submit the comment. Being ajax, it will have better performance as it allows your method to just return the comments so you can replace the old ones, or even just the newly added one so it can be added to the bottom of the list. Having said that, this solution does rely on the use of javascript and preferably jquery-unobtrusive-ajax to work and render decent looking client code. You don't have to know javascript to use this method thanks to the AjaxOptions class which has some powerful yet easy to use options built into it.

How to create a custom Html.ValidationMessage?

This is my Controller:
/// <summary>
/// Activity
/// </summary>
/// <returns></returns>
public ActionResult CreateActivity()
{
AriaCRMEntities aria = new AriaCRMEntities();
var unit = from u in aria.ActivityGroupIDs select u;
List<SelectListItem> lst = new List<SelectListItem>();
foreach (var u in unit)
{
lst.Add(new SelectListItem { Text = u.ActivityGroupName.ToString(), Value = u.ActivityGroupID_FK.ToString() });
}
ViewData["activity"] = lst;
return View();
}
[HttpPost]
public ActionResult CreateActivity(FormCollection model)
{
if (ModelState.IsValid)
{
Activity activ = new Activity();
if (!string.IsNullOrEmpty(model["ActivityGroupID_FK"]))
{
AriaCRMEntities aria = new AriaCRMEntities();
activ.ActivityGroupID_FK = Int32.Parse(model["ActivityGroupID_FK"]);
activ.ActivityType = model["ActivityType"];
activ.ActivityDes = model["ActivityDes"];
aria.Activities.AddObject(activ);
aria.SaveChanges();
return RedirectToAction("Create");
}
}
return View(model);
}
This is my View :
<% using (Html.BeginForm()) {%>
<%: Html.ValidationSummary(true,) %>
<fieldset>
<legend>Fields</legend>
<br />
<%:Html.DropDownListFor(model=>model.ActivityGroupID_FK , (IEnumerable<SelectListItem>)ViewData["activity"], "انتخاب نوع فعالیت")%><br />
<%Html.ValidationMessageFor (model=>model.ActivityGroupID_FK,"dddddddddd"); %>
<div class="editor-label">
<%: Html.LabelFor(model => model.ActivityType) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.ActivityType) %>
<%: Html.ValidationMessageFor(model => model.ActivityType,"dfsaaa") %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.ActivityDes) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.ActivityDes) %>
<%: Html.ValidationMessageFor(model => model.ActivityDes) %>
</div>
<p>
<input type="submit" value="Create" id="btn"/>
</p>
</fieldset>
<% } %>
But now I need to validate <%:Html.DropDownListFor%>. How do I create custom validation?
If you want to display a validation error message next to your drop-down list, you can do this from your controller like so:
ModelState.AddModelError("ActivityGroupID_FK", "The selected value is invalid.");
Update
I just noticed the validation message for your DropDownList looks like this:
<%Html.ValidationMessageFor (model=>model.ActivityGroupID_FK,"dddddddddd"); %>
You might want to change that like so:
<%: Html.ValidationMessageFor(model=>model.ActivityGroupID_FK,"dddddddddd") %>

Empty List When Data Is Posted To Action Method (ASP .Net MVC)

I have got a problem with submitting data to Post method.
I have a class similar to this:
public class A
{
public int Id{get; set;}
public string Name{get; set;}
public List<B> Bs {get; set;}
}
In the View I have a list of Bs object that I can add to the Bs property of class A without any problem. I can see the result without any problem in my Razor View as well.
The problem occurs when I post the final result to my Add Action Method to save them in my database.
When the final object is sent to my Action method the Bs property is completely empty. So what I did instead was to add that collection to a TempData dictionary and retrieved it in my Add Action method.
My question is simple why is my Bs property empty when its posted to my Edit action method ? The reason why I ask this is because in my AddBs action method I add my Bs to my Model and send it back to View and everything is find up to that point.
Edit:
#SLaks
This is the code I have in my View :
<% using (Html.BeginForm("Create", "A"))
{ %>
<%: Html.ValidationSummary(true) %>
<div class="editor-label">
<%: Html.LabelFor(model => model.Name) %>
</div>
<div class="editor-field">
<%: Html.EditorFor(model => model.Name) %>
<%: Html.ValidationMessageFor(model => model.Name) %>
</div>
<% if (Model.Bs.Any())
{ %>
<h3>B List</h3>
<%= Html.Telerik().Grid(Model.Bs)
.Name("CheckedOrders")
.Columns(columns =>
{
columns.Bound(d => d.Id).Width(100);
columns.Bound(d => d.Name).Width(300);
})
.Footer(false)
%>
<% } %>
<br />
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
This is the HTML generated:
<form action="/Admin/BusinessType/Create" method="post">
<fieldset>
<div class="editor-label">
<label for="Name">Name</label>
</div>
<div class="editor-field">
<input class="text-box single-line" id="Name" name="Name" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="Name" data-valmsg-replace="true"></span>
</div>
<div class="t-widget t-grid" id="CheckedOrders"><table cellspacing="0"><colgroup><col style="width:100px" /><col style="width:300px" /></colgroup><thead class="t-grid-header"><tr><th class="t-header" scope="col">Id</th><th class="t-last-header t-header" scope="col">Name</th></tr></thead><tbody><tr><td>2</td><td class="t-last">Heeey</td></tr><tr class="t-alt"><td>3</td><td class="t-last">Testtttt</td></tr></tbody></table></div>
<br />
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
</form>
The Code above shows perfectly fine in my Browser, I can even see the Bs list, but when I type a Name And Click on Create, the Bs list is empty.
I think the secret to this is in the view. I have a similar model in my app. In the view you have to format the names of the controls correctly:
<%
for (int i=0; i<model.Bs.Count; i++)
{
Html.TextBox( string.Format( "Bs[{0}].Property1OfB", i), model.Bs.Property1OfB );
Html.TextBox( string.Format( "Bs[{0}].Property2OfB", i), model.Bs.Property2OfB );
}
%>
The Add method will only receive whatever <input> elements are in the form that posted to it.

Asp.Net MVC - How to reload form data if 'Save' of data fails in a view containing mutilple forms

I have a page which allows a user to edit the details of a Contact. It shows basic details (name, address etc.), a list of known email addresses and a list of telephone numbers and allows them to add/edit email and telephone details. My view has three forms, one for the basic details, one for the email details and one for the telephone details - each one posts to a different controller action (SaveDetails, SaveEmail, SaveTelephone). The problem is if the user goes to add a new email and the ModelState is not valid then once I return to the main Details (HttpGet) controller method to show the page and the model state error then I have lost the data that the user entered into the email form.
I don't really want to have one big form containing all the details and I want to avoid using TempData to store/pass the form input back to the main page - does anyone have any suggestions for how to achieve this?
Here is my view model ContactDetailsViewModel which contains the EmailDetailsViewModel and TelephoneFaxDetailsViewModel:
public class ContactDetailsViewModel : MasterViewModel
{
public int ContactId { get; set; }
public int? EmailId { get; set; }
public int? TelephoneFaxId { get; set; }
[DisplayName("First Name")]
[StringLength(30, ErrorMessage = "First Name cannot be more than 30 characters")]
public string FirstName { get; set; }
[DisplayName("Last Name")]
[StringLength(30, ErrorMessage = "Last Name cannot be more than 30 characters")]
public string LastName { get; set; }
public IList<EmailSummary> EmailSummaries { get; set; }
public IList<TelephoneFaxSummary> TelephoneFaxSummaries { get; set; }
public EmailDetailsViewModel EmailDetails { get; set; }
public TelephoneFaxDetailsViewModel TelFaxDetails { get; set; }
}
and my view which contains 3 separate forms and is strongly typed to the ContactDetailsViewModel:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<SPP.Portal.ViewModels.Contacts.ContactDetailsViewModel>" %>
<% using (Html.BeginForm("Details","Contact", FormMethod.Post))
{%>
<%: Html.ValidationSummary() %>
<fieldset>
<%: Html.HiddenFor(m => m.EmailId) %>
<%: Html.HiddenFor(m => m.TelephoneFaxId) %>
<div class="editor-field">
<div class="left">
<%: Html.LabelFor(model => model.FirstName) %>
<%: Html.TextBoxFor(model => model.FirstName)%>
<%: Html.ValidationMessageFor(model => model.FirstName)%>
</div>
<div class="right">
<%: Html.LabelFor(model => model.LastName) %>
<%: Html.TextBoxFor(model => model.LastName)%>
<%: Html.ValidationMessageFor(model => model.LastName)%>
</div>
</div>
<p>
<input type="button" onclick="addEmail();" id="email" value="Add New Email" />
<input type="button" onclick="addTelephoneFax();" id="telFax" value="Add New Telephone/Fax" />
<input name="button" id="Save"type="submit" value="Save" />
<input name="button" id="Return" type="submit" value="Return" />
</p>
<%
} %>
<fieldset>
<legend>All Email Details</legend>
<table>
<tr>
<th>Edit</th>
<th>Email Address</th>
<th>Email Type</th>
<th>Delete</th>
</tr>
<% foreach (var emailSummary in Model.EmailSummaries)
{ %>
<tr>
<%: Html.Hidden(emailSummary.EmailId.ToString()) %>
<td>
Edit
</td>
<td>
<%: emailSummary.EmailAddress %>
</td>
<td>
<%: emailSummary.EmailType %>
</td>
<td>
Delete
</td>
</tr>
<% } %>
</table>
</fieldset>
<% using (Html.BeginForm("EmailDetails", "Contact", FormMethod.Post))
{%>
<div id="EmailDetails">
<fieldset>
<legend>Email</legend>
<%: Html.HiddenFor(m => m.EmailDetails.Id)%>
<%: Html.HiddenFor(m => m.EmailDetails.ContactId)%>
<div class="editor-field">
<div class="right">
<%: Html.LabelFor(m => m.EmailDetails.EmailAddress)%>
<%: Html.TextBoxFor(m => m.EmailDetails.EmailAddress)%>
<%: Html.ValidationMessageFor(model => model.EmailDetails.EmailAddress)%>
</div>
</div>
<div class="editor-field">
<div class="left">
<%: Html.LabelFor(m => m.EmailDetails.EmailType)%>
<%: Html.DropDownListFor(m => m.EmailDetails.EmailType, Model.EmailDetails.EmailTypeCodes)%>
<%: Html.ValidationMessageFor(model => model.EmailDetails.EmailType)%>
</div>
<p>
<input type="submit" id="EmailSave" value="Save" />
<input type="submit" name="button" id="EmailCancel" value="Cancel" />
</p>
</fieldset>
<% } %>
</div>
<fieldset>
<legend>All Telephone/Fax Details</legend>
<table>
<tr>
<th>Edit</th>
<th>Dialing Code</th>
<th>Telephone/Fax Number</th>
<th>Delete</th>
</tr>
<% foreach (var telFaxSummary in Model.TelephoneFaxSummaries)
{ %>
<tr>
<%: Html.Hidden(telFaxSummary.TelephoneFaxId.ToString())%>
<td>
<a href="<%:ResolveUrl(telFaxSummary.EditUrl.OriginalString)%>" >Edit</a>
</td>
<td>
<%: telFaxSummary.DialingCode%>
</td>
<td>
<%: telFaxSummary.TelephoneFaxNumber%>
</td>
<td>
Delete
</td>
</tr>
<% } %>
</table>
</fieldset>
<% using (Html.BeginForm("TelephoneFaxDetails", "Contact", FormMethod.Post))
{%>
<div id="TelephoneFaxDetails">
<%: Html.HiddenFor(m => m.TelFaxDetails.TelephoneFaxId)%>
<%: Html.HiddenFor(m => m.TelFaxDetails.ContactId)%>
<fieldset>
<legend>Telephone/Fax</legend>
<div class="editor-field">
<div id="left" class="left">
<%: Html.LabelFor(m => m.TelFaxDetails.InternationalDialingCode)%>
<%: Html.TextBoxFor(m => m.TelFaxDetails.InternationalDialingCode)%>
<%: Html.ValidationMessageFor(model => model.TelFaxDetails.InternationalDialingCode)%>
</div>
<div id="middle" class="right">
<%: Html.LabelFor(m => m.TelFaxDetails.TelephoneFaxNumber)%>
<%: Html.TextBoxFor(m => m.TelFaxDetails.TelephoneFaxNumber)%>
<%: Html.ValidationMessageFor(model => model.TelFaxDetails.TelephoneFaxNumber)%>
</div>
</div>
<p>
<input type="submit" id="TelephoneSave" value="Save" />
<input type="submit" name="button" id="TelephoneCancel" value="Cancel" />
</p>
</fieldset>
<% } %>
</div>
And then my controller method to add a new Email:
[HttpPost]
public ActionResult EmailDetails(ContactDetailsViewModel contactViewModel)
{
var viewModel = contactViewModel.EmailDetails;
if (ModelState.IsValid)
{
try
{
_viewModelService.UpdateEmail(User, viewModel);
return RedirectToAction("Details");
}
catch (Exception ex)
{
ModelState.AddModelError("Exception", "The Email was not updated.");
return RedirectToAction("Details");
}
}
return View("Details", contactViewModel);
}
So the idea is that if I take the whole ContactViewModel into the Email post then if the model is not valid then I should be able to return the whole Details ContactViewModel which would contain the basic contact details and the email details that the user had just input so they would not lose the data. The email details are passed to the controller fine however all of the basic contact data is null when passed into the controller - any ideas?
IMO, this will not be feasible as far as your scenario, I mean when you posting 2nd Form(EmailDetails) you only posting that FORM only, so what ever comes within the SCOPE of it,it only goes to SERVER.
So in such case, you could opt for Ajax to save/validate individual field set(Email,All Telephone/Fax Detail), or individual ascx.
Your options are limited, I am afraid:
You either have to post one large
form, that's the only option to keep
all values.
Or you can have only one
of them editable at a time. But
forcing the user to post three times
is probably not what you want.
One approach, which will keep your code a little nicer, is to redesign the model to have three properties. One for each 'form':
public class ContactDetailsViewModel
{
public ContactDetails Basics { get; set; }
public EmailDetails Email { get; set; }
public PhoneDetails Phone { get; set; }
}
You can then create a partial view for each property. Or a custom editor template. That will keep your main markup a little cleaner.

In MVC, one Edit HttpPost is working, the other one isn't. What am I missing?

Googling generally and on SO hasn't helped me yet, so:
I am building my first MVC application from scratch, going by the MVC Music Store example but instead building a little application where arena Fighters can be created and made to Fight each other. (Fighters and Fight have been made linked to underlying tables through EF).
I have controllers for both the Fighters and the Fights. The Edit Actionresult for Fights is working, but for Fighters it is not. When I hit the button to save my alterations I return to the associated Index page, but no changes have been committed. This is my question: why is this failing?
From BarracksController, with the faulty non-updating HttpPost Edit (should have been named FighterController, but neverthemind):
//
// GET: /Barracks/Edit
public ActionResult Edit(int id)
{
ViewData.Model = _FightDb.Fighters.Single(f => f.Id == id);
return View();
}
//
// POST: /Barracks/Edit
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
var fighter = _FightDb.Fighters.Single(f => f.Id == id);
try
{
UpdateModel(fighter, "Fighter");
var x = ViewData.GetModelStateErrors();
_FightDb.SaveChanges();
return RedirectToAction("Index");
}
catch
{
var viewModel = fighter;
return View(viewModel);
}
}
(As you can see, I've included the GetModelStateErrors trick from this SO question, but the result for x is null)
This is the controller that does work, FightController:
//
// GET: /Fights/Edit
public ActionResult Edit(int id)
{
var viewModel = new FightDetailsViewModel
{
Fight = _FightDb.Fights.Single(f => f.ID == id),
Fighters = _FightDb.Fighters.ToList()
};
return View(viewModel);
}
//
// POST: /Fights/Edit
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
var fight = _FightDb.Fights.Single(f => f.ID == id);
try
{
UpdateModel(fight, "Fight");
_FightDb.SaveChanges();
return RedirectToAction("Index");
}
catch
{
var viewModel = new FightDetailsViewModel
{
Fight = _FightDb.Fights.Single(f => f.ID == id),
Fighters = _FightDb.Fighters.ToList()
};
return View(viewModel);
}
}
This is edit.aspx for the Fighters: (Edited after comment)
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Mvc3_EF_BW_Fight.Models.Fighter>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="cphMain" runat="server">
<h2>Edit</h2>
<%: Html.EditorForModel() %>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="head" runat="server">
</asp:Content>
Which uses the following Fighter.ascx in Shared:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Mvc3_EF_BW_Fight.Models.Fighter>" %>
<% using (Html.BeginForm())
{%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fighter</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.Id) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Id) %>
<%: Html.ValidationMessageFor(model => model.Id) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.FighterName) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.FighterName) %>
<%: Html.ValidationMessageFor(model => model.FighterName) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.FighterStyleDescription) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.FighterStyleDescription) %>
<%: Html.ValidationMessageFor(model => model.FighterStyleDescription) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.FighterLongDescription) %>
</div>
<div class="editor-field">
<%: Html.TextAreaFor(model => model.FighterLongDescription) %>
<%: Html.ValidationMessageFor(model => model.FighterLongDescription) %>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
This is the edit.aspx for Fights
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Mvc3_EF_BW_Fight.ViewModels.FightDetailsViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="cphMain" runat="server">
<h2>Edit</h2>
<%: Html.EditorFor(model => model.Fight, new { Fighters = Model.Fighters })%>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="head" runat="server">
</asp:Content>
And this is the Fight.ascx:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Mvc3_EF_BW_Fight.Models.Fight>" %>
<% using (Html.BeginForm())
{%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.ID) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.ID) %>
<%: Html.ValidationMessageFor(model => model.ID) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.FightName) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.FightName) %>
<%: Html.ValidationMessageFor(model => model.FightName) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Fighter1ID) %><br />
<%: Html.LabelFor(model => model.Fighter1Reference.Value.FighterName)%>
</div>
<div class="editor-field">
<%: Html.DropDownList("Fighter1ID", new SelectList(ViewData["Fighters"] as IEnumerable, "ID", "FighterName", Model.Fighter1ID))%>
<%: Html.ValidationMessageFor(model => model.Fighter1ID) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Fighter2ID) %>
</div>
<div class="editor-field">
<%: Html.DropDownList("Fighter1ID", new SelectList(ViewData["Fighters"] as IEnumerable, "ID", "FighterName", Model.Fighter1ID))%>
<%: Html.ValidationMessageFor(model => model.Fighter2ID) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Fighter1Login) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Fighter1Login) %>
<%: Html.ValidationMessageFor(model => model.Fighter1Login) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Fighter2Login) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Fighter2Login) %>
<%: Html.ValidationMessageFor(model => model.Fighter2Login) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.FightStatusID) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.FightStatusID) %>
<%: Html.ValidationMessageFor(model => model.FightStatusID) %>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
And this is my viewmodel for Fights:
public class FightDetailsViewModel
{
public Fight Fight { get; set; }
public List<Fighter> Fighters { get; set; }
}
There is no ViewModel for Fighters (none that is involved in this scenario, anyway).
I can post any code you may wish to see.
Edit: I've looked at Using ViewModel Pattern with MVC 2 Strongly Typed HTML Helpers and ASP.NET MVC 2 UpdateModel() is not updating values in memory or database , but i haven't seen a solution there yet.
Instead of this UpdateModel(fighter, "Fighter"); try calling the updte model just like this UpdateModel(fighter);. The difference is between the two edits that in case of Fighter your model is directly the Fighter so you do not need the name, while in case of the Fight you call the Editor for model.Fight so you need the name. See this question as well: asp.net mvc2 - how to get model and model.something in the same way in controller?

Resources