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(),
Related
On my view I want to show a CheckBoxList that will show the names of all the students. Checking each student will ultimately mark their attendence. What I want to do is pass a ViewBag that (supposedly) contains a SelectList of students.
Here is what I am trying to do in Controller.
public ActionResult Create(int? id)
{
List<SelectListItem> selectListItems = new List<SelectListItem>();
var #class = db.Classes.Find(id);
var students = #class.Students.ToList();
foreach (Student student in students)
{
SelectListItem item = new SelectListItem()
{
Text = student.St_name,
Value = student.St_id.ToString(),
Selected = student.IsSelected
};
selectListItems.Add(item);
}
ViewBag.students = selectListItems;
var timetables = #class.Timetables.ToList();
ViewBag.Sat_ti_fk_id = new SelectList(timetables, "Ti_id", "Ti_day");
return View();
}
And this is what I am doing in my View
<div class="form-group">
#Html.LabelFor(model => model.Sat_st_fk_id, "Sat_st_fk_id", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.ListBoxFor(ViewBag.students, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.ValidationMessageFor(model => model.Sat_st_fk_id, "", new { #class = "text-danger" })
</div>
</div>
But the ListBoxFor method is showing error that says it takes 2 arguments.
Any sort of help is apreciated.
Thanks.
#Html.ListBoxFor Takes three arguments
The property that value are stored in
List of data to show as SelectList
Html attributes ( optional )
You should change the #Html.ListBoxFor to this
#Html.ListBoxFor(model => model.Sat_st_fk_id, ViewBag.students as SelectList, new { #class = "control-label col-md-2" })
I'm trying to populate a dropdown list with concatenated items to be used in a form . I have gotten the dropdown list to display those concatenated strings but I think it's failing to get an ID. (I'can't submit the form, dropdown blinks when I click submit), I take it this means no ID is being passed
Controller:
public ActionResult AdminCreate()
{
var query = (from e in db.Employees
select e.FullName + "-" + e.EmployeeID + "-" + e.Site.SiteName);
SelectList empList = new SelectList(query);
ViewBag.empList = empList;
return View();
}
View:
<div class="form-group">
#Html.LabelFor(model => model.EmployeeID, "Employee", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor((model => model.Employee.EmployeeID), (SelectList)ViewBag.empList, "--Select--", htmlAttributes: new { #class = "form-control"})
#Html.ValidationMessageFor(model => model.EmployeeID, "", new { #class = "text-danger" })
</div>
</div>
Make your query and SelecList as follows:
var employeeList = db.Employees.Select(e => new
{
e.EmployeeID
FullName = e.FullName + "-" + e.EmployeeID + "-" + e.Site.SiteName
}).ToList()
SelectList empList = new SelectList(employeeList,"EmployeeID","FullName");
In your #Html.DropDownListFor replace model.Employee.EmployeeID with model.EmployeeID as follows:
#Html.DropDownListFor(model => model.EmployeeID, (SelectList)ViewBag.empList, "--Select--", htmlAttributes: new { #class = "form-control"})
This question already has an answer here:
How to keep cascade dropdownlist selected items after form submit?
(1 answer)
Closed 5 years ago.
I'm using MVC , C# and Entity Framework.
The object on my model are:
State-------- Id , Name
City ------- Id , Name , StateId
TheObject----Id, Name, StateId, CityId
I want to create an edit form for TheObject.
The Edit form has 2 dropdownlist State and City that are created dynamically , and the City list depend on selection made on State List.
The problem is that the dropdown list are filled correctly , but when the edit form is open these 2 dropdownlist are in empty state and does not have selected the real values for the object that is edited .
The partial code for Edit view is this :
<div class="form-group">
#Html.LabelFor(u => u.State, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(u => u.State,
new SelectList(ViewBag.State, "Id", "Name"),
"Choose State",
new { #class = "form-control", #onchange = "selectCities()" })
#Html.ValidationMessageFor(u => u.State, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(u => u.City, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(u => u.City,
new SelectList(Enumerable.Empty<SelectListItem>(), "Id", "Name"),
"Choose City",
new { #class = "form-control" })
#Html.ValidationMessageFor(u => u.City, "", new { #class = "text-danger" })
</div>
</div>
function selectCities() {
debugger;
var stateId = $("#State").val();
$.ajax({
url: '/Home/selectCities',
type: 'POST',
datatype: 'application/json',
contentType: 'application/json',
data: JSON.stringify({ stateId: +stateId }),
success: function (result) {
$("#City").html("");
$("#City").append
($('<option></option>').val(null).html("---choose City---"));
$.each($.parseJSON(result), function (i, cty)
{ $("#City").append($('<option></option>').val(cty.Id).html(cty.Name)) })
},
error: function () { alert("Error !") },
});
}
The partial code of the controller is this :
private void Fill_StateDropDownList()
{
var st = from d in db.States
orderby d.Name
select d;
ViewBag.State = st.ToList();
}
[HttpPost]
public ActionResult selectCities(string stId)
{
List < City > lstcity = new List < City > ();
int stateiD = Convert.ToInt32(stId);
lstgrupet = (from d in db.Citys
where d.StateID==stateiD
select d).ToList();
string result= JsonConvert.SerializeObject(lstgrupet, Formatting.Indented,
new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore});
return Json(result, JsonRequestBehavior.AllowGet);
}
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
TheObject obj = db.TheObjects.Find(id);
if (obj == null)
{
return HttpNotFound();
}
Fill_StateDropDownList()
return View(obj);
}
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public ActionResult EditPost(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var theobjectToUpdate = db.TheObjects.Find(id);
if (TryUpdateModel(theobjectToUpdate, "",
new string[] { "Name","StateId","CityId" }))
{
try
{
db.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception)
{
ModelState.AddModelError("", "Error.");
}
}
Fill_StateDropDownList()
return View(theobjectToUpdate);
}
actually SelectList has a construct with a parameter called selectedValue
now you should know how to do it
in edit View
<div class="form-group">
#Html.LabelFor(u => u.State, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(u => u.State,
new SelectList(ViewBag.State, "Id", "Name", Model.State),
"Choose State",
new { #class = "form-control", #onchange = "selectCities()" })
#Html.ValidationMessageFor(u => u.State, "", new { #class = "text-danger" })
</div>
</div>
You already have the saved state and city values. All you have to do is to load the subset of cities based on the saved state id and use that collection to render the dropdown for cities
The DropDownListFor helper method will select the corresponding option from the SELECT element as long as the view model's City property value matches with one of the option's value attribute value.
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
TheObject obj = db.TheObjects.Find(id);
if (user == null)
{
return HttpNotFound();
}
Fill_StateDropDownList()
FillCities(obj.State);
return View(obj);
}
private void FillCities(int stateId)
{
ViewBag.CityList = db.Cities
.Where(g => g.StateId== stateId)
.Select(f => new SelectListItem() {
Value = f.Id.ToString(),
Text = f.Name })
.ToList();
}
Make an adjustment to the view
var cityList = new List<SelectListItem>();
if (ViewBag.CityList != null)
{
cityList =ViewBag.CityList as List<SelectListItem>;
}
#Html.DropDownListFor(u => u.City, cityList , "Choose City")
Another option is to make an ajax call on the page load (document ready to get the cities based on the value of state dropdown). It gets complicated if you have multiple nested dropdowns.(ends up in callback hell)
I'm new to MVC, and very new to JQuery. I'm attempting to populate textboxes based on a dropdownlist selection. My Product model contains the fields ProductId, Name, and Price. I want to populate the ProductId and Price fields in my QuoteDetails based upon the product Name chosen. My controller action is as follows:
public ActionResult AddProduct(int quoteId, int quoteDetailId)
{
var items = db.Products.ToList();
ViewBag.ProductData = items;
ViewData["QuoteId"] = quoteId;
ViewData["QuoteDetailId"] = quoteDetailId;
return PartialView("EditQuoteDetail", new QuoteDetail { QuoteId = quoteId, QuoteDetailId = quoteDetailId, ProductId = 1, ProductName = " ", Amount = 1, ListPrice = 0, Discount = 0, Price = 0 });
}
The relevant portion of the partial view EditQuoteDetail is as follows:
#Html.HiddenFor(model => model.QuoteId, new { htmlAttributes = new { #class = "form-control" } })
#Html.HiddenFor(model => model.QuoteDetailId, new { htmlAttributes = new { #class = "form-control" } })
#Html.EditorFor(model => model.ProductId, new { htmlAttributes = new { #id="ProductId", #class = "form-control" } })
#Html.DropDownList("ProductName", new SelectList(ViewBag.ProductData, "Name", "Name"), new { #id = "ProductName" })
#Html.EditorFor(model => model.Amount, new { htmlAttributes = new { #class = "form-control" } })
#Html.EditorFor(model => model.ListPrice, new { htmlAttributes = new { #id="Price", #class = "form-control" } })
#Html.EditorFor(model => model.Discount, new { htmlAttributes = new { #class = "form-control" } })
#Html.EditorFor(model => model.Price, new { htmlAttributes = new { #class = "form-control" } })
The script I am using to attempt to populate the ProductId and Price fields is as follows:
<script type="text/javascript">
$(document).ready(function () {
$('#ProductName').change(function () {
$('#ProductId').val($(this).val());
$('#Price').val($(this).val());
});
});
</script>
But when I make the dropdown list selection, nothing happens. What am I doing wrong? Any help would be much appreciated.
Here's what I think is happening...
(1) #Html.DropDownList("ProductName", new SelectList(ViewBag.ProductData, "Name", "Name"), new { #id = "ProductName" })
This line creates a <select> html element with an id of "ProductName" as expected; though the value of the options within that list are text values. Because you are using the "Name" for both the value and text of the option. For example:
<select id="ProductName" name="ProductName">
<option value="Product 1">Product 1</option>
<option value="Product 2">Product 2</option>
</select>
(2) #Html.EditorFor(model => model.ProductId, new { htmlAttributes = new { #id="ProductId", #class = "form-control" } })
Since you are using the EditorFor Html helper, it is trying to validate an integer (I assume) of the ProductId. Your javascript is trying to insert a String, like "Product 1".
(3) #Html.EditorFor(model => model.ListPrice, new { htmlAttributes = new { #id="Price", #class = "form-control" } })
This has a slightly different issue. The ID of the HTML element will default to "ListPrice" and not be overridden by your #id property in the htmlAttributes object. Side question, do you mean to put #id = "Price" on the "ListPrice" element? Even if you fixup the ID attributes of these elements, you may still run into the data type issue from (2) above.
Try switching the target element to a TextBoxFor as a quick test.
The problem is not in the script you are populating dropdown
ViewBag.ProductData, "Name", "Name" by Name so the id of the dropdown will also be its Name and also ProductId and Price both are int so you cannot set text value in int field
So you should set the ViewBag.ProductData, "Id", "Name" when ever you will run the script it will get the int value of productId
Edit
if you want to get the data based on your Product id you have to make ajax call for that in jquery and you have to make action for that in controller
[HttpPost]
public ActionResult GetProduct(int pId)
{
var data = db.Products.Find(id);
return Json(data);
}
and your view would be
#model CMSUsersAndRoles.Models.QuoteDetail
#{
ViewBag.Title = "EditQuoteDetail";
Layout = null;
}
#{
var quoteId = (int)ViewData["QuoteId"];
var quoteDetailId = (int)ViewData["QuoteDetailId"];
}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<body>
<div id="row">
<table>
#using (Html.BeginCollectionItem("quoteDetail"))
{
<tr>
#Html.HiddenFor(model => model.QuoteId, new { htmlAttributes = new { #class = "form-control" } })
#Html.HiddenFor(model => model.QuoteDetailId, new { htmlAttributes = new { #class = "form-control" } })
#Html.TextBoxFor(model => model.ProductId, new { htmlAttributes = new { #id="ProductId", #class = "form-control" } })
#Html.DropDownList("ProductList", new SelectList(ViewBag.ProductData, "ProductId", "Name"), new { #id = "ProductList" })
#Html.EditorFor(model => model.Amount, new { htmlAttributes = new { #class = "form-control" } })
#Html.TextBoxFor(model => model.Price, new { htmlAttributes = new { #id="Price", #class = "form-control" } }
#Html.EditorFor(model => model.Discount, new { htmlAttributes = new { #class = "form-control" } })
#Html.EditorFor(model => model.ListPrice, new { htmlAttributes = new { #class = "form-control" } })
#Ajax.ActionLink(" ", "DeleteProduct", "QuoteViewModel", new { quoteId = Model.QuoteId, quoteDetailId = (Model.QuoteDetailId) },
new AjaxOptions
{
HttpMethod = "POST",
Confirm = "Are you Sure You Want to Delete " + Model.ProductName,
OnSuccess = "RemoveRow"
},
new { #class = "btn btn-danger glyphicon glyphicon-trash" })
</tr>
}
</table>
</div>
<script type="text/javascript">
$(document).ready(function () {
$('#ProductList').change(function () {
$.post("/QuoteViewModel/GetProduct", { pId: $(this).val() }, function (data) {
$('#ProductId').val(data.ProductId);
$('#Price').val(data.Price);
});
});
});
</script>
</body>
</html>
At last, I found an answer. travis.js started me on the right path when he said the BeginCollectionItem helper was overtaking my HTML, so I had to use id contains syntax to make it work. The working jQuery (in the parent view) is as follows:
<script type="text/javascript">
$(document).ready(function () {
$(document).on("change", '[id*="ProductList"]', function () {
$.post("/QuoteViewModel/GetProduct", { pId: $(this).val() }, function (data) {
$("[id*='ProductId']").val(data.ProductId);
$("[id*='Price']").val(data.Price);
});
});
});
</script>
And the controller action (thanks Usman) is as follows:
[HttpPost]
public ActionResult GetProduct(int pId)
{
var data = db.Products.Find(pId);
return Json(data);
}
Whew!
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.)