How can I keep values of dropdownlist after return from a submit?
I have the following structure.
On controller:
[HttpPost]
public ActionResult Cadastro(CadastroViewModel viewModel)
{
try
{
var usuario = Mapper.Map<CadastroViewModel, Usuario>(viewModel);
servicoDeAplicacaoDeUsuario.Inserir(usuario);
}
catch (ValidationException validationException)
{
this.ModelState.Clear();
foreach (var error in validationException.Errors)
this.ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
return View("Cadastro", viewModel);
}
return RedirectToAction("Index", "Home");
}
So above it will return some validation errors, but the viewModel does not have the filled list (dropdown) to return.
#using (Html.BeginForm("Cadastro", "Cadastro", FormMethod.Post))
{
<ul class="login-signup-list">
<li>
#Html.TextBoxFor(f => f.NomeCompleto, new { #class = "span10", autofocus = "autofocus", placeholder = RecursoDeCadastro.NomeCompleto, maxlength = 100 })
</li>
<li>
#Html.TextBoxFor(f => f.Email, new { #class = "span10", type="email", placeholder = RecursoDeCadastro.Email, maxlength = 100 })
</li>
<li>
#Html.TextBoxFor(f => f.Senha, new { #class = "span10", type="password", placeholder = RecursoDeCadastro.Senha, maxlength = 50 })
</li>
<li>
#Html.DropDownListFor(m => m.IdPais, new SelectList(Model.Paises, "Id", "Nome"), new { #class = "span10" } )
</li>
<li>
#Html.DropDownListFor(m => m.IdUF, new SelectList(Model.UFs, "Id", "Nome"), new { #class = "span10" } )
</li>
<li>
#Html.DropDownListFor(m => m.IdMunicipio, new SelectList(Model.Municipios, "Id", "Nome"), new { #class = "span10" } )
</li>
<li>
<button class="btn btn-primary">#RecursoDeCadastro.CriarConta</button>
</li>
// ... ?
}
I could take every list from database again and set it´s value using the Id (IdUF for example) that came with viewModel on post, because i'm losing only the list. But I think that it´s not necessary that request to database again.
Is there a workaround for it?
Can I use session by logged user? How?
Thanks.
Wilton Ruffato Wonrath
Related
I do already have dropdownlist in my ASP.NET MVC 5 project.
I need to remove one of the item parameter call it "Admin"; I want remove it from the list when the page is loaded.
This is my Razor markup:
<div class="form-group">
#Html.LabelFor(model => model.RoleName, htmlAttributes: new { #class = "control-label col-md-3" })
<div class="col-md-9">
#Html.DropDownListFor(model => model.RoleName, Model.VMRoles, new { #class = "form
control input-sm", multiple= "multiple" })
#Html.ValidationMessageFor(model => model.RoleName, "", new { #class = "text-danger" })
</div>
</div>
And this the C# controller:
[HttpGet]
[Authorize]
public ActionResult Create()
{
var vm = new CreateUserViewModel
{
VMSisterConcerns = _sisterConcernService.GetAllSisterConcern().Select(c => new SelectListItem { Text = c.Name, Value = c.ConcernID.ToString() }).ToList(),
VMRoles = _roleService.GetAllRole().Select(r => new SelectListItem { Text = r.Name, Value = r.Name }).ToList(),
ConcernId = User.Identity.GetConcernId().ToString()
};
return View(vm);
}
And this the model:
public ICollection<System.Web.Mvc.SelectListItem> VMRoles { get; set; }
Here is the correct answer thanks for #itsme86 he mention How to understand LINQ .
VMRoles = _roleService.GetAllRole().Where(r => r.name != "Admin").Select(r => new SelectListItem { Text = r.Name, Value = r.Name }).ToList(),
I’m trying to create multiselect DropDownList, which should load data from the database. All fields should be selected on the first run. I'd like to send back to the controller parameter List<string> selectedStatus, do some logic there and send back to the view by ViewData["Status"]. Based on this parameter I'd like to fill multiselect DropDownList... I’m using jQuery select2.
On first run there is nothing in the DropDownList selected.
When I select more than one option and send to controller, the first value only will appear in the DropDownList.
I have the Index method with the following code
var status = new List<SelectListItem>();
if (!selectedStatus.Any())
{
status = (from t in _context.Status
select new SelectListItem()
{
Text = t.Status,
Value = t.StatusId.ToString(),
Selected = true
}).OrderBy(t => t.Text)
.ToList();
}
else
{
status = (from t in _context.Status
select new SelectListItem()
{
Text = t.Status,
Value = t.StatusId.ToString(),
Selected = false
}).OrderBy(t => t.Text)
.ToList();
foreach (var item in status)
{
if (selectedStatus.Any(s => s.Contains(item.Value)) == true)
{
item.Selected = true;
}
}
}
ViewData["Status"] = status;
In the Index view
<form asp-controller="Home" asp-action="Index" method="get">
<div class="input-group">
#Html.DropDownList("selectedStatus", new SelectList((IEnumerable<SelectListItem>)ViewData["Status"], "Value", "Text"), htmlAttributes: new { #class = "form-control", multiple = "multiple", id = "select" })
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="submit">Submit</button>
<a asp-action="Index" class="btn btn-outline-secondary">Cancel</a>
</div>
</div>
</form>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");
<script>
$('select').select2({
theme: 'bootstrap4',
multiple: true,
});
</script>
}
}
What am I doing wrong? What's missing there?
Thank you.
As Fabian Kamp suggested , you should use a viewmodel to contain the Status and selectedStatus. Here is a workaround that using js to selected all fields , you could refer to:
ViewModel
public class Status
{
public int StatusId { get; set; }
public string StatusName { get; set; }
}
public class StatusVM
{
public List<string> selectedStatus { get; set; }
public List<SelectListItem> Status { get; set; }
}
Controller
public IActionResult MultiSelectDropDownList(List<string> selectedStatus)
{
var model = new StatusVM();
if (!selectedStatus.Any())
{
model.Status = (from t in _context.Status
select new SelectListItem()
{
Text = t.StatusName,
Value = t.StatusId.ToString(),
}).OrderBy(t => t.Text)
.ToList();
model.selectedStatus = _context.Status.Select(s => s.StatusId.ToString()).ToList();
}
else
{
model.Status = (from t in _context.Status
select new SelectListItem()
{
Text = t.StatusName,
Value = t.StatusId.ToString(),
}).OrderBy(t => t.Text)
.ToList();
model.selectedStatus = new List<string>();
foreach (var item in model.Status)
{
if (selectedStatus.Any(s => s.Contains(item.Value)) == true)
{
model.selectedStatus.Add(item.Value);
}
}
}
return View(model);
}
View
#model Demo1.Models.StatusVM
<form asp-controller="Home" asp-action="MultiSelectDropDownList" method="get">
<div class="input-group">
#Html.DropDownListFor(m => m.selectedStatus, new SelectList((IEnumerable<SelectListItem>)Model.Status, "Value", "Text", Model.selectedStatus), htmlAttributes: new { #class = "js-example-theme-multiple", multiple = "multiple", id = "select" })
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="submit">Submit</button>
<a asp-action="Index" class="btn btn-outline-secondary">Cancel</a>
</div>
</div>
</form>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>
<script>
var stringArray = #Html.Raw(Json.Serialize(Model.selectedStatus));
$(document).ready(function () {
$("select").select2({
theme: "classic",
multiple: true,
});
$("select").val(stringArray).trigger('change');
});
</script>
}
}
Result
Reference:Setting multiple values using jQuery select2
On the first run its empty because you have a not equals char infront of your selectedStatus.Any() or you have accendently changed the true false in the first if, but a easier solution for this would be
var status = (from t in _context.Status
select new SelectListItem()
{
Text = t.Status,
Value = t.StatusId.ToString(),
Selected = !selectedStatus.Any()
}).OrderBy(t => t.Text)
.ToList();
without a if query.
The second thing is you should really use a ViewModel not ViewData or some thing like this for this amount of data.
Every time I try to edit a record that I have created in my ASP.NET MVC project, when I click save the fields are nullified.
Here is part of the code for my Edit Get and post in my controller:
Review Controller:
// GET: Reviews/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Review review = db.Reviews.Find(id);
if (review == null)
{
return HttpNotFound();
}
ViewBag.ReviewID = new SelectList(db.Reviews, "ReviewID", "Rating", review.ReviewID);
return View(review);
}
// POST: Reviews/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ReviewID,Username,WellnessService,Rating,Feedback,Date")] Review review)
{
if (ModelState.IsValid)
{
db.Entry(review).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("ReviewEdit");
}
ViewBag.ReviewID = new SelectList(db.Reviews, "ReviewID", "Rating", review.ReviewID);
return View(review);
}
Here is the code for my view called Edit:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>#Html.DisplayFor(model => model.WellnessService)</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.ReviewID)
<div class="form-group">
#Html.LabelFor(model => model.Rating, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10" align="left">
#{
List<SelectListItem> listItems = new List<SelectListItem>();
listItems.Add(new SelectListItem { Text = "1", Value = "1" });
listItems.Add(new SelectListItem { Text = "2", Value = "2" });
listItems.Add(new SelectListItem { Text = "3", Value = "3" });
listItems.Add(new SelectListItem { Text = "4", Value = "4" });
listItems.Add(new SelectListItem { Text = "5", Value = "5" });
listItems.Add(new SelectListItem { Text = "6", Value = "6" });
listItems.Add(new SelectListItem { Text = "7", Value = "7" });
}
#Html.DropDownListFor(model => model.Rating, listItems, "Please choose value")
#Html.ValidationMessageFor(model => model.Rating, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Feedback, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.Feedback, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Feedback, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to My Reviews", "ReviewEdit", new { #class = "btn btn-success btn-lg" })
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Help would greatly be appreciated
Thanks in advance
Don't have a dev pc to hand to check this, but are you sure review has data in it. If it has, can you be sure that the following line works?
db.Entry(review).State = EntityState.Modified;
Don't you need to attach review to the context?
What happens if you temporarily do something like;
if (ModelState.IsValid)
{
var foo = db.Entry.Where(x=>x.reviewId == review.reviewId).First();
// then manually set all the parameters
db.SaveChanges();
return RedirectToAction("ReviewEdit");
}
Its not elegant, but if that works, and your data is updated, at least you know its the way you are attaching the object to the context.
I'll try and get a better example later when I can get to a dev workstation.
I'm trying to upload a file alongside with some model information.
In my table I already have a field 'image' (string) to save the relative URL to the image.
But I don't really know how to do the uploading itself.
I've already read a lot off tutorials, but they all use HttpPostedFileBase, which isn't supported anymore?
This is what I have thus far:
Upload page:
#using (Html.BeginForm("Lets", "Create", FormMethod.Post, new { #enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<fieldset>
<div class="form-group">
<div class="mdl-cell col-md-10 mdl-textfield mdl-js-textfield">
#Html.LabelFor(m => m.Lets.Name, new { #class="mdl-textfield__label" })
#Html.TextBoxFor(m => m.Lets.Name, new { #class= "form-control mdl-textfield__input" })
</div>
</div>
<div class="form-group">
<div class="mdl-cell col-md-10 mdl-textfield mdl-js-textfield">
#Html.LabelFor(m => m.Lets.Images)
<input type="file" name="LetsImages" id="m.Lets.Images" /> <br />
</div>
</div>
<div class="form-group">
<div class="mdl-cell col-md-10 mdl-textfield mdl-js-textfield">
#Html.LabelFor(m => m.Lets.Description, new { #class="mdl-textfield__label" })
#Html.TextBoxFor(m => m.Lets.Description, new { #class= "form-control mdl-textfield__input" })
</div>
</div>
<div class="form-group">
<div class="mdl-cell col-md-10 mdl-textfield mdl-js-textfield">
#Html.LabelFor(m => m.Lets.Credits, new { #class="mdl-textfield__label" })
#Html.TextBoxFor(m => m.Lets.Credits, new { #class= "form-control mdl-textfield__input" })
</div>
</div>
<div class="form-group">
<div class="mdl-cell col-md-10">
#Html.LabelFor(m => m.Lets.Group)
#Html.DropDownListFor(m => m.Lets.GroupId, new SelectList(Model.Groups, "Id", "Name"), "-- Selecteer een Groep --", new { #class= "form-control" })
</div>
</div>
#Html.ActionLink("Terug naar het overzicht", "Index", new { }, new { #class= "mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect" })
<input type="submit" value="Save" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect" />
</fieldset>
}
Controller:
[HttpGet]
public IActionResult Create()
{
var model = new LetsViewModel
{
Lets = new Lets(),
Groups = _kletsContext.Groups.AsEnumerable(),
Letses = _kletsContext.Lets.AsEnumerable().OrderBy(m => m.Name)
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(LetsViewModel model)
{
LetsViewModel viewModel = null;
try
{
if(!ModelState.IsValid)
throw new Exception("The Lets model is not valid!");
var letsImage = "INSERT LOGIC FOR IMAGEUPLOAD HERE?";
model.Lets.UserId = User.GetUserId();
model.Lets.StatusId = 1;
model.Lets.Images = letsImage;
_kletsContext.Lets.Add(model.Lets);
if (_kletsContext.SaveChanges() == 0)
{
throw new Exception("The Lets model could not be saved!");
}
//Success(CreateMessage(ControllerActionType.Create, "klets", model.Name), true);
return RedirectToAction("Index", "Home");
}
catch(Exception ex)
{
ModelState.AddModelError(string.Empty, "Unable to save changes.");
viewModel = new LetsViewModel
{
Lets = model.Lets,
Letses = _kletsContext.Lets.AsEnumerable().OrderBy(m => m.Name)
};
}
return View(viewModel);
}
I've added the place where I think the logic should come?
So what I want to do is:
Upload the Image to a folder
Rename it
Store the relative path as a string to the db.
Thank you
There is no HttpPostedFileBase in MVC6, you have to use IFormFile like this:
public FileDetails UploadSingle(IFormFile file)
{
FileDetails fileDetails;
using (var reader = new StreamReader(file.OpenReadStream()))
{
var fileContent = reader.ReadToEnd();
var parsedContentDisposition = ContentDispositionHeaderValue.Parse(file.ContentDisposition);
fileDetails = new FileDetails
{
Filename = parsedContentDisposition.FileName,
Content = fileContent
};
}
return fileDetails;
}
You can upload images using handlers , link to tutorials are
http://www.c-sharpcorner.com/blogs/uploading-files-using-jquery-ajax-in-asp-net1
http://www.binaryintellect.net/articles/f2a2f1ee-e18a-416b-893e-883c800f83f4.aspx
http://www.dotnetjalps.com/2011/12/async-file-upload-with-jquery-and.html?m=1
These tutorials are using jquery to pass the image to the handler and then handler saving the image in the folder. You can rename the image before uploading to the folder and get back the saved image name in response and then save image name along with other model properties.
I eventually got it working by:
include using Microsoft.AspNet.Hosting; and using System.IO;
using System.Net.Http.Headers;
Add public IHostingEnvironment _environment { get; set;}
to your controller (I added it to my commoncontroller, and then it's available to me on my controller)
Then in my create:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(LetsViewModel model, IFormFile LetsImages)
{
LetsViewModel viewModel = null;
try
{
if(!ModelState.IsValid)
throw new Exception("The Lets model is not valid!");
if(LetsImages != null)
{
var targetDirectory = Path.Combine(_environment.WebRootPath, string.Format("images/uploads"));
var fileName = ContentDispositionHeaderValue
.Parse(LetsImages.ContentDisposition)
.FileName
.Trim('"');
var savePath = Path.Combine(targetDirectory, fileName);
var relPath = "/images/uploads/" + fileName;
try
{
LetsImages.SaveAs(savePath);
model.Lets.Images = relPath;
}
catch
{
throw new Exception("There was a problem with saving the file!");
}
}
else
{
model.Lets.Images = null;
}
model.Lets.UserId = User.GetUserId();
model.Lets.StatusId = 1;
_kletsContext.Lets.Add(model.Lets);
if (_kletsContext.SaveChanges() == 0)
{
throw new Exception("The Lets model could not be saved!");
}
//Success(CreateMessage(ControllerActionType.Create, "klets", model.Name), true);
return RedirectToAction("Index", "Home");
}
catch(Exception ex)
{
ModelState.AddModelError(string.Empty, "Unable to save changes.");
viewModel = new LetsViewModel
{
Lets = model.Lets,
Groups = _kletsContext.Groups.AsEnumerable(),
Letses = _kletsContext.Lets.AsEnumerable().OrderBy(m => m.Name)
};
}
return View(viewModel);
}
Just make sure you've created the folder where the images should belong, else it's not going to work without any error!
Thanks everybody for your help!
I have two forms: one for assigning roles to users and the other one for removing roles from users. They're strikingly similar, both the views and the controllers. Here they are (the form itself):
AssignRole.cshtml
#using (Html.BeginForm("AssignRole", "User", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(m => m.UserID)
<div class="form-group">
#Html.LabelFor(m => m.UserName, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.UserName, new { #class = "form-control", #readonly = "readonly" })
#Html.ValidationMessageFor(m => m.UserName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.RoleName, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.RoleName, new SelectList(Model.UnassignedRoles, "Value", "Text"), Resources.DropdownSelect, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="#Resources.Assign" class="btn btn-default" />
</div>
</div>
}
RemoveRole.cshtml
#using (Html.BeginForm("RemoveRole", "User", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(m => m.UserID)
<div class="form-group">
#Html.LabelFor(m => m.UserName, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.UserName, new { #class = "form-control", #readonly = "readonly" })
#Html.ValidationMessageFor(m => m.UserName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.RoleName, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.RoleName, new SelectList(Model.AssignedRoles, "Value", "Text"), Resources.DropdownSelect, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="#Resources.Remove" class="btn btn-default" />
</div>
</div>
}
Finally, here's the controller with the actions they make use of:
UserController.cs
//
// GET: /User/AssignRole
[Authorize(Roles = "Admin")]
[HttpGet]
public ActionResult AssignRole(string userID)
{
var user = context.Users.Where(u => u.Id == userID).FirstOrDefault();
var vm = new UserAssignRoleViewModel();
vm.UserID = user.Id;
vm.UserName = user.UserName;
List<IdentityRole> unassignedRoles = new List<IdentityRole>();
foreach (var role in context.Roles)
{
if (this.UserManager.IsInRole(vm.UserID, role.Name) == false)
{
unassignedRoles.Add(role);
}
}
vm.UnassignedRoles = unassignedRoles.OrderBy(r => r.Name).Select(rr => new SelectListItem { Value = rr.Name.ToString(), Text = rr.Name }).ToList();
return View(vm);
}
//
// POST: /User/AssignRole
[Authorize(Roles = "Admin")]
[HttpPost]
public ActionResult AssignRole(UserAssignRoleViewModel vm)
{
this.UserManager.AddToRole(vm.UserID, vm.RoleName);
ViewBag.ResultMessage = Resources.RoleAssignedSuccessfully;
List<IdentityRole> unassignedRoles = new List<IdentityRole>();
foreach (var role in context.Roles)
{
if (this.UserManager.IsInRole(vm.UserID, role.Name) == false)
{
unassignedRoles.Add(role);
}
}
vm.UnassignedRoles = unassignedRoles.OrderBy(r => r.Name).Select(rr => new SelectListItem { Value = rr.Name.ToString(), Text = rr.Name }).ToList();
return View(vm);
}
//
// GET: /User/RemoveRole
[Authorize(Roles = "Admin")]
[HttpGet]
public ActionResult RemoveRole(string userID)
{
var user = context.Users.Where(u => u.Id == userID).FirstOrDefault();
var vm = new UserRemoveRoleViewModel();
vm.UserID = user.Id;
vm.UserName = user.UserName;
vm.AssignedRoles = context.Roles.OrderBy(r => r.Name).ToList().Select(rr => new SelectListItem { Value = rr.Name.ToString(), Text = rr.Name }).ToList();
List<IdentityRole> assignedRoles = new List<IdentityRole>();
foreach (var role in context.Roles)
{
if (this.UserManager.IsInRole(vm.UserID, role.Name) == true)
{
assignedRoles.Add(role);
}
}
vm.AssignedRoles = assignedRoles.OrderBy(r => r.Name).Select(rr => new SelectListItem { Value = rr.Name.ToString(), Text = rr.Name }).ToList();
return View(vm);
}
//
// POST: /User/RemoveRole
[Authorize(Roles = "Admin")]
[HttpPost]
public ActionResult RemoveRole(UserRemoveRoleViewModel vm)
{
if (this.UserManager.IsInRole(vm.UserID, vm.RoleName))
{
this.UserManager.RemoveFromRole(vm.UserID, vm.RoleName);
ViewBag.ResultMessage = Resources.RoleUnassignedSuccessfully;
List<IdentityRole> assignedRoles = new List<IdentityRole>();
foreach (var role in context.Roles)
{
if (this.UserManager.IsInRole(vm.UserID, role.Name) == true)
{
assignedRoles.Add(role);
}
}
}
else
{
ViewBag.ResultMessage = Resources.ThisUserDoesNotBelongToSelectedRole;
}
return View (vm);
}
Here's the issue:
The dropdown has to get repopulated everytime, either assigning roles to users or removing them. Everything works fine in the assigning roles side; it only shows the unassigned roles in the dropdown, and when you add a role, coming back from the POST action it shows the refreshed dropdown without the role you just assigned.
But in the removing roles side, as soon as you remove a role from a user (which it DOES correctly), coming back to the view from the POST action it throws the exception
Value cannot be null. Parameter name: items
in the line
#Html.DropDownListFor(m => m.RoleName, new SelectList(Model.AssignedRoles, "Value", "Text"), Resources.DropdownSelect, new { #class = "form-control" })
My guess is that since in the POST Action method RemoveRole I'm not changing in any way the RoleName property of the UserRemoveRoleViewModel, and coming back to the view the dropdown has been repopulated, the m => m.RoleName crashes because it's looking for the already removed role, which is not in the list anymore. Hope I'm explaining myself well enough.
The problem is that I have absolutely no idea on how to fix this. Any help?
It looks like the second method, for removals, is missing a line which re-initializes model object with roles list. Since ASP.NET MVC is stateless, this re-initialization has to be done on each request, even if returned view is the same.
Add this line:
vm.AssignedRoles = assignedRoles.OrderBy(r => r.Name).Select(rr => new SelectListItem { Value = rr.Name.ToString(), Text = rr.Name }).ToList();
right after foreach ends in RemoveRole and the error should go away.
The problem is you've never assigned the assignedRoles collection to your view property (AssignedRoles).
Also, you can make your code cleaner by using a LINQ statement to create your list.
In your GET action, you fill up the AssignedRoles property of your view model. In the POST action you fail to do that. So when it gets to the view code it is null and the helper can't make a dropdown list out of a null collection. You need to populate that in the POST action. (Note that it doesn't stay populated between the GET and POST actions.)