Привязка к коллекции CheckboxFor
Я пытаюсь реализовать экран разрешений, в котором пользователю может быть дано определенное разрешение на конкретном экране. Для этого я создаю коллекцию Checkboxfor, привязанную к коллекции свойств bool. Но когда я отправляю форму, я получаю либо все свойства bool true, либо все false, в зависимости от того, инициализировал ли я эти свойства как true или false в конструкторе viewmodel.
Вот код для ViewModel:
Подход I:
public class MyViewModel
{
public MyModel Model { get; set; }
public IEnumerable<ScreenType> Screens { get; set; }
public IEnumerable<SecurityType> SecurityTypes { get; set; }
public List<PermissionType> Permissions { get; set; }
public MyViewModel()
{
LoadScreens();
LoadSecurityTypes();
LoadPermissions();
}
public void LoadPermissions()
{
Permissions = new List<PermissionType>();
foreach (var screen in Screens)
{
foreach (var securityType in SecurityTypes)
{
Permissions.Add(
new PermissionType
{
PermissionId= Guid.NewGuid(),
ScreenId= screen.Id,
SecurityId = securityType.Id,
IsAllowed = false
});
}
}
}
}
Подход II
public class MyViewModel
{
public MyModel Model { get; set; }
public IEnumerable<ScreenType> Screens { get; set; }
public IEnumerable<SecurityType> SecurityTypes { get; set; }
public List<bool> AllowedList { get; set; }
public MyViewModel()
{
LoadScreens();
LoadSecurityTypes();
LoadPermissions();
}
public void LoadPermissions()
{
AllowedList = new List<bool>();
foreach (var form in Forms)
{
foreach (var security in Securities)
{
AllowedList.Add(false);
}
}
}
}
Вот код моего представления:
Подход I:
@using (Ajax.BeginForm("Create", "Role", null, new AjaxOptions { UpdateTargetId = "addStatus", InsertionMode = InsertionMode.Replace, OnSuccess = "onFormPostSuccess" }, new { @id = "AddForm" }))
{
<div>
<span><label>Screen</label></span>
@foreach (var security in Model.SecurityTypes)
{
<span><label>@security.Name</label></span>
}
<br />
@foreach (var screen in Model.Screens)
{
<span>@screen.Name</span>
foreach (var security in Model.SecurityTypes)
{
<span>@Html.CheckBoxFor(m => m.Permissions.Where(s => s.SecurityId == security.Id && s.ScreenId == screen.Id).Single().IsAllowed, new { @id = HtmlHelper.GenerateIdFromName("Create" + screen.Name + security.Name) })</span>
}
<br />
}
</div>
<div>
<span>
<input type="image" src="/content/images/submit_button.png" value="submit" />
</span>
</div>
<div>
<span id="addStatus" class="submitStatus"></span>
</div>
}
Подход II:
@using (Ajax.BeginForm("Create", "Role", null, new AjaxOptions { UpdateTargetId = "addStatus", InsertionMode = InsertionMode.Replace, OnSuccess = "onFormPostSuccess" }, new { @id = "AddForm" }))
{
<div>
<span><label>Screen</label></span>
@foreach (var security in Model.SecurityTypes)
{
<span><label>@security.Name</label></span>
}
<br />
@foreach (int i=0; i < Model.Screens.Count(); i++)
{
<span>@Model.Screens.ElementAt(i).Name</span>
foreach (int j=0; j<Model.SecurityTypes.Count(); j++)
{
@* The index 5*i+j is because I have 5 security types *@
<span>@Html.CheckBoxFor(Model.AllowedList[5*i+j], new { @id = HtmlHelper.GenerateIdFromName("Create" + @Model.Screens.ElementAt(i).Name + @Model.SecurityTypes.ElementAt(j).Name) })</span>
}
<br />
}
</div>
<div>
<span>
<input type="image" src="/content/images/submit_button.png" value="submit" />
</span>
</div>
<div>
<span id="addStatus" class="submitStatus"></span>
</div>
}
Вот код для метода действия Create в Controller:
[Authorize]
[HttpPost]
public JsonResult Create(MyViewModel viewModel)
{
if ( ModelState.IsValid)
{
if (service.AddRole(viewModel))
{
return Json("Role Added !");
}
return Json("Role exists !");
}
return Json("Please correct errors");
}
Когда я проверяю viewModel в методе Create, все свойства IsAllowed имеют значение false. Как были инициализированы в конструкторе модели представления. Нет эффекта от установки / снятия флажков в представлении.
Я что-то пропустил?
1 ответ
HTML-помощники со строгой типизацией, такие как CheckBox, работают только с простыми выражениями в качестве первого аргумента (доступ к индексу свойств и массивов не более). Следующее выражение совершенно не соответствует возможностям этого помощника:
m => m.Permissions.Where(s => s.SecurityId == security.Id && s.ScreenId == screen.Id).Single().IsAllowed
Я бы порекомендовал вам использовать реальную модель представления и выполнять это на уровне вашего контроллера при отображении между вашей моделью домена и моделью представления.
ОБНОВИТЬ:
Очевидно, мой ответ о моделях представления был недостаточно ясен, поэтому позвольте мне показать, что я имею в виду под моделями представления.
Итак, начнем с определения моделей нашего представления, которые потребуются для реализации необходимой логики для этого представления:
public class CreateViewModel
{
public IEnumerable<ScreenViewModel> Screens { get; set; }
}
public class ShowCreateViewModel: CreateViewModel
{
public IEnumerable<SecurityTypeViewModel> SecurityTypes { get; set; }
}
public class ScreenViewModel
{
public string ScreenName { get; set; }
[HiddenInput(DisplayValue = false)]
public int ScreenId { get; set; }
public IEnumerable<SecurityTypeViewModel> SecurityTypes { get; set; }
}
public class SecurityTypeViewModel
{
public string SecurityName { get; set; }
[HiddenInput(DisplayValue = false)]
public int SecurityTypeId { get; set; }
public bool IsAllowed { get; set; }
}
Тогда у нас может быть действие контроллера, которое позаботится о получении моделей доменов из репозитория или чего-то подобного и сопоставлении с моделями представлений:
public class HomeController : Controller
{
public ActionResult Create()
{
// The information will obviously come from a domain model that
// we map to a view model, but for the simplicity of the answer
// I am hardcoding the values here
var securityTypes = new[]
{
new SecurityTypeViewModel { SecurityTypeId = 1, SecurityName = "security 1" },
new SecurityTypeViewModel { SecurityTypeId = 2, SecurityName = "security 2" },
new SecurityTypeViewModel { SecurityTypeId = 3, SecurityName = "security 3" },
};
// The information will obviously come from a domain model that
// we map to a view model, but for the simplicity of the answer
// I am hardcoding the values here
return View(new ShowCreateViewModel
{
SecurityTypes = securityTypes,
Screens = new[]
{
new ScreenViewModel
{
ScreenId = 1,
ScreenName = "Screen 1",
SecurityTypes = securityTypes
},
new ScreenViewModel
{
ScreenId = 2,
ScreenName = "Screen 2",
SecurityTypes = securityTypes
},
}
});
}
[HttpPost]
public ActionResult Create(CreateViewModel model)
{
// The view model passed here will contain all the necessary information
// for us to be able to perform the actual Save:
// a list of the screen ids along with a list of the selected permission ids
return Content(
"Thank you for selecting the following allowed permissions:<br/>" +
string.Join("<br/>", model.Screens.Select(s => string.Format(
"screen id: {0}, permission ids: {1}",
s.ScreenId,
string.Join(",", s.SecurityTypes.Where(st => st.IsAllowed).Select(st => st.SecurityTypeId))
)))
);
}
}
и теперь осталось определить представление и соответствующие шаблоны редактора / дисплея.
Начнем с основного вида (~/Views/Home/Create.cshtml
):
@model ShowCreateViewModel
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
@using (Ajax.BeginForm("Create", "Home", null, new AjaxOptions { UpdateTargetId = "addStatus", InsertionMode = InsertionMode.Replace, OnSuccess = "onFormPostSuccess" }, new { @id = "AddForm" }))
{
<table>
<thead>
<tr>
<th>Screen/Security type</th>
@Html.DisplayFor(x => x.SecurityTypes)
</tr>
</thead>
<tbody>
@Html.EditorFor(x => x.Screens)
</tbody>
</table>
<div>
<input type="submit" value="submit" />
</div>
<div id="addStatus" class="submitStatus"></div>
}
Далее у нас есть шаблон редактора для ScreenViewModel
модель (~/Views/Shared/EditorTemplates/ScreenViewModel.cshtml
):
@model ScreenViewModel
<tr>
<td>
@Html.DisplayFor(x => x.ScreenName)
@Html.EditorFor(x => x.ScreenId)
</td>
@Html.EditorFor(x => x.SecurityTypes)
</tr>
Затем шаблон редактора для SecurityTypeViewModel
модель (~/Views/Shared/EditorTemplates/SecurityTypeViewModel.cshtml
):
@model SecurityTypeViewModel
<td>
@Html.CheckBoxFor(x => x.IsAllowed)
@Html.EditorFor(x => x.SecurityTypeId)
</td>
И, наконец, шаблон отображения для SecurityTypeViewModel
модель (~/Views/Shared/DisplayTemplates/SecurityTypeViewModel.cshtml
):
@model SecurityTypeViewModel
<th>
@Html.DisplayFor(x => x.SecurityName)
</th>
И это в значительной степени это:
Я оставил для вас сопоставление между вашими фактическими моделями доменов и моделями представлений, определенными здесь.