This is my model
public class AdministrationModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public bool IsApproved { get; set; }
}
This is my controller
public ActionResult GetTabContent(string id)
{
switch (id)
{
case "tab3":
model = GetAllUsersInfo();
viewName = "Administration";
break;
}
return View(viewName);
}
private List<AdministrationModel> GetAllUsersInfo()
{
List<AdministrationModel> userList = new List<AdministrationModel>();
foreach (MembershipUser user in Membership.GetAllUsers())
{
UserProfile userProfile = UserProfile.GetUserProfile(user.UserName);
userList.Add(new AdministrationModel { EmailAddress = user.Email, IsApproved = user.IsApproved, FirstName = userProfile.FirstName, LastName = userProfile.LastName });
}
return userList;
}
This is my View
@model List<AdminContainerModel>
@using (Html.BeginForm("Administration", "Account"))
{
<fieldset>
<div>
@foreach (AdministrationModel AM in Model)
{
<div>
<div class="colFull">@Html.DisplayFor(modelItem => AM.FirstName)</div>
<div class="colFull">@Html.DisplayFor(modelItem => AM.LastName)</div>
<div class="colFull">@Html.DisplayFor(modelItem => AM.EmailAddress)</div>
<div class="colPartial"><input type="checkbox" checked="@AM.IsApproved"/> </div>
<div class="clear"></div>
</div>
}
</div>
<input type="submit" value="Update Account" />
</fieldset>
}
When the user clicks the Update Account button it goes to the controller
[HttpPost]
public ActionResult Administration(List<AdministrationModel> model)
{
return View();
}
inside this method, model is always null. however the View that renders everything is perfect and shows what I want it to show. What am I doing wrong?
Matty's answer will work.
If you want to avoid having to specify indexes (which wouldn't work if you had an ICollection) you can define a child template as Xander has explained.
The benefit is you still get all your strong typed intellisense in the AdminContainerModel View and MVC hooks the items back in to your List on post back for you out of the box.
Here's an example:
Main Editor template (as Xander said):
AdminContainerModel Editor template(This is called for each item in your List because you've called @Html.EditorForModel:
If you want to return list items with the model on submit you will need to use a for loop, not a foreach
When using collections, in order to correctly process them so they are model-bound on post without any additional leg work, you need to make sure they are indexed correctly, you can do this by using a for loop, something like:
That should model bind without any other code :)
Edit: Sorry I forgot, displayFors by default don't put the correct properties on for model binding, added hiddenFors for the other fields that don't have an editorFor
Edit2: Based on your other question in the comment, if it was public facing and you didn't want them to change any of the hidden for values using the dev tools, try the following:
Ok so you don't want them to change the hiddenFors, that's fine, but you will need some sort of ID so you know which client is which when the data is posted, I suggest that instead of having these in the above code:
Replace them with:
That way you're not posting back the firstname, lastname or email address, just a reference to the actual client that is ticked, unticked.
To answer your other question in the comment about keeping track of the original values, in your controller method you can just go and get the original values from the database, then here's how you can detect which ones are different, something like:
Your
@model
directive is incorrect, it should be the fully qualified type name.In this case:
After your updated question.
Try using:
This will call a specialized Editor Template for your list.
http://blogs.msdn.com/b/nunos/archive/2010/02/08/quick-tips-about-asp-net-mvc-editor-templates.aspx