I am trapped in ViewModel binding and Refresh scenario.
Scenario:
As Administrator I want to create User Account and based on selected Role from DropDownList refresh FORM to select by checkboxes specific accesses (bool) connected with Role.
So user can have one single role with default access list that can be modified by Admin when he create Account.
My ViewModel:
public class CreateViewModel
{
public User User { get; set; }
public IEnumerable<Role> Roles { get; set; }
public IEnumerable<RoleAccess> RoleAcceses { get; set; }
public Role SelectedRole { get; set; }
public void Refresh()
{
User.UserAccesses.Clear();
foreach (var item in RoleAcceses.Where(x => x.RoleId == SelectedRole.Id))
{
User.UserAccesses.Add(new UserAccess { UserId = User.Id, Access = item.Access, AccessId = item.AccessId, Value = item.Value });
}
}
}
As you can see in above code I have RoleAccess collection with default values for specific Role. Refresh method clears actual UserAccess collection and repopulate it for SelectedRole.
My questions are:
- Is it possible to do this without jQuery? What is your opinion?
- If it is possible to refresh FORM without jQuery how to post it without loosing Binded values entered by Admin?
Here I can NOT Add other @BeginForm because I will lose actual data context right? - If jQuery is only solution how to write Controller Action that would check for jQuery Filtering ?
- How jQuery function should looks like to replace Binded to ViewModel checkboxes?
My courrent controller Action:
public class AccountController : Controller
{
IAccountService accountService;
public AccountController()
{
accountService = new AccountService();
}
[HttpGet]
public ActionResult Create(int role = 1)
{
IEnumerable<RoleAccess> roleAccesses = null;
IEnumerable<Role> roles = null;
roleAccesses = accountService.GetRoleAcgesses();
roles = accountService.GetRoles();
var createViewModel = new CreateViewModel();
createViewModel.RoleAcceses = accountService.GetRoleAccesses();
createViewModel.Roles = accountService.GetRoles();
createViewModel.SelectedRole = createViewModel.Roles.FirstOrDefault(x => x.Id == role);
createViewModel.Refresh();
return View(createViewModel);
}
public ActionResult Refresh(int role)
{
return RedirectToAction("Create", "Account", new { role });
}
[HttpPost]
public ActionResult Create([Bind(Prefix = "User")] Models.User user, FormCollection formsCollection)
{
var viewModel = new CreateViewModel();
UpdateModel<CreateViewModel>(viewModel, new[] { "User.Login", "User.Password", "User.FirstName", "User.LastName" });
viewModel.User.RoleId = Convert.ToInt32(formsCollection["Role"]);
if (ModelState.IsValid)
{
bool result = accountService.CreateAccount(viewModel.User);
if (!result)
{
// SEND INFO ABOUT FAILURE
}
else
{
return RedirectToAction("list");
}
}
return View();
}
[HttpGet]
public ActionResult List()
{
var accounts = accountService.GetAccounts();
var viewModel = new ListViewModel() { Accounts = accounts };
return View(viewModel);
}
}
My View:
@model VDOT.Web.AccountViewModels.CreateViewModel
@{
ViewBag.Title = "Create";
}
<h2>Create</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
@using (Html.BeginForm("create", "account"))
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<fieldset>
<legend>CreateViewModel</legend>
<fieldset>
<div class="editor-label">
@Html.LabelFor(model => model.User.Login)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.User.Login)
@Html.ValidationMessageFor(model => model.User.Login)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.User.Password)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.User.Password)
@Html.ValidationMessageFor(model => model.User.Password)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.User.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.User.FirstName)
@Html.ValidationMessageFor(model => model.User.FirstName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.User.LastName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.User.LastName)
@Html.ValidationMessageFor(model => model.User.LastName)
</div>
<div class="editor-field">
<select id="paging" onchange="location.href=this.value">
@foreach (var item in Model.Roles)
{
<option value="@Url.Action("create", "account", new { role = item.Id })">
@item.Name
</option>
}
</select>
</div>
<div class="editor-field">
@foreach (var item in Model.User.UserAccesses)
{
@Html.CheckBox(item.Access.Name, item.Value);
<br />
}
</div>
</fieldset>
</fieldset>
<fieldset>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
Thank you for any help!
I think you are looking for a way to bind your user accesses collection to your user view model. With this in place your complete model would be persistent on post back allowing you to achieve whatever functionality required without scripting (although you could then use jQuery, if available, to enhance the page for “smoother” functionality).
A great method for creating model list property bindings is available on Steven Sanderson’s blog, here.