问题
I'm trying to think of a way to manage the selection of a matrix of values via view models in MVC 5.
I have a list of companies and a list of roles. The roles are the same for each company. What I want is to output a matrix of companies/roles with a checkbox for each combination. This allows the user to select which role the person will have for each company.
I can render the output with nested foreach loops but I can't help but think there's a better way to achieve this with MVC and Editor Templates.
Below is a crude example of the layout. So every company is output as a column and every role is output as a row. A checkbox for each company is then output into every row.
Company/Role COMPANY1 COMPANY2 COMPANY3
ROLE1 [X] [] []
ROLE2 [] [] [X]
ROLE3 [] [] []
The added complexity is being able to handle postback. My view model structure is currently..
public class RequestViewModel
{
public Guid Id { get; set; }
public List<CompanyAccessViewModel> CompanyAccessViewModels { get; set; }
}
public class CompanyAccessViewModel
{
public int Company { get; set; }
public Guid Id { get; set; }
public Guid RequestId { get; set; }
public List<Company> ListCompanies { get; set; }
public List<Role> ListRoles { get; set; }
public List<CompanyAccessRoleViewModel> CompanyAccessRoleViewModels { get; set; }
}
public class CompanyAccessRoleViewModel
{
public Guid AccessRequirementId { get; set; }
public Guid Id { get; set; }
public string Role { get; set; }
}
ListCompanies is the list of all the companies. ListRoles is a list of all the roles.
回答1:
As always, start with a view model(s) to represent what you want to display/edit.
public class RoleVM
{
public int ID { get; set; }
public string Name { get; set; }
public List<CompanyVM> Companies { get; set; }
}
public class CompanyVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
and in the GET method
[HttpGet]
public ActionResult Index()
{
IEnumerable<Role> roles = db.Roles;
IEnumerable<Company> companies = db.Companies;
List<RoleVM> model = roles.Select(x => new RoleVM()
{
ID = x.ID,
Name = x.Name,
Companies = companies.Select(y => new CompanyVM()
{
ID = y.ID,
Name = y.Name
}).ToList()
}).ToList();
// For editing existing roles, set the IsSelected property
// of the respective CompanyVM to true
return View(model);
}
and the view
@model List<RoleVM>
@using (Html.BeginForm())
{
<table>
<thead>
<tr>
<th>Company/Role</th>
@foreach(var company in Model.FirstOrDefault().Companies)
{
<th>@company.Name</th>
}
</tr>
</thead>
<tbody>
@for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>
@Model[i].Name
@Html.HiddenFor(m => m[i].ID)
@Html.HiddenFor(m => m[i].Name)
</td>
@for (int j = 0; j < Model[i].Companies.Count; j++)
{
<td>
@Html.HiddenFor(m => m[i].Companies[j].ID)
@Html.HiddenFor(m => m[i].Companies[j].Name)
@Html.CheckBoxFor(m => m[i].Companies[j].IsSelected)
</td>
}
</tr>
}
</tbody>
</table>
<button type="submit">Save</button>
}
which will the post to
[HttpPost]
public ActionResult Index(List<RoleVM> model)
{
// To get the companies for each role
foreach (var role in model)
{
var selectedCompanies = role.Companies.Where(x => x.IsSelected);
Note that using nested foreach
loops would never have bound correctly when submitting your form (you need to use for
loops). If you want to use EditorTemplates (your properties can be IEnumerable<T>
rather that IList<T>
, then create a partial in /Views/Shared/EditorTemplates/RoleVM.cshtml
@model RoleVM
<tr>
<td>
@Model.Name
@Html.HiddenFor(m => m.ID)
@Html.HiddenFor(m => m.Name)
</td>
@Html.EditorFor(m => m.Companies)
</tr>
and another for /Views/Shared/EditorTemplates/CompanyVM.cshtml
@model CompanyVM
<td>
@Html.HiddenFor(m => m.ID)
@Html.HiddenFor(m => m.Name)
@Html.CheckBoxFor(m => m.IsSelected)
</td>
and the main view would be
@model List<RoleVM>
.... // as above
<tbody>
@Html.EditorFor(m => m)
</tbody>
来源:https://stackoverflow.com/questions/37729936/creating-a-matrix-of-checkboxes-which-supports-post-back-in-asp-net-mvc