Populating a Dropdownlistfor with multiple ids - c#

I am using a dropdownlistfor html helper to pick ingredients in a dish. I can get it to display all the ingredients, i can make it post the value's, but i cant seem to populate it when i want to edit a dish with existing ingredients.
My controller:
public ActionResult Edit(int id)
{
DishViewModel dvm = new DishViewModel();
dvm.Dish = facade.GetDishGateway().Get(id);
dvm.Dish.Ingredients.ForEach(x => dvm.SelectedIds.Add(x.Id));
return View(dvm);
}
My viewmodel:
public class DishViewModel
{
Facade facade = new Facade();
public DishViewModel()
{
SelectedList = facade.GetIngredientGateway().GetAll().FirstOrDefault().Id.ToString();
SelectedIds = new List<int>();
}
public Dish Dish { get; set; }
public List<int> SelectedIds { get; set; }
public string SelectedList { get; set; }
public List<SelectListItem> SelectList
{
get
{
List<SelectListItem> temp = new List<SelectListItem>();
foreach (var item in facade.GetIngredientGateway().GetAll())
{
temp.Add(new SelectListItem { Text = item.Name + ": " + item.Price + ",-", Value = item.Id.ToString() });
}
return temp;
}
}
}
My View:
<div class="form-group row">
#Html.LabelFor(model => model.Dish.Ingredients, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.SelectedIds, Model.SelectList, new { #multiple = "multiple"})
</div>
</div>
By my understanding, the model => model.selectedIds is the one populating the dropdownlistfor, but it does not seem to work

Html.DropDownListFor renders a standard select element, where only one option may be marked as selected.
If you want a select multiple, then you should use Html.ListBoxFor.

Related

Unable to provide input for a selected attribute of a drop down list in MVC

public partial class Department
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Department()
{
this.Employees = new HashSet<Employee>();
}
public int ID { get; set; }
public string Name { get; set; }
public Nullable<bool> IsSelected { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Employee> Employees { get; set; }
}
public ActionResult Create()
{
List<SelectListItem> DeptList = new List<SelectListItem>();
foreach (Department tempdept in db.Departments)
{
SelectListItem sli = new SelectListItem
{
Text = tempdept.Name,
Value = tempdept.ID.ToString(),
Selected = tempdept.IsSelected.HasValue ? tempdept.IsSelected.Value : false
};
DeptList.Add(sli);
}
ViewBag.Departments = DeptList;
return View();
}
<div class="form-group">
#Html.LabelFor(model => model.DepartmentID, "DepartmentID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.DepartmentID,(IEnumerable<SelectListItem>)ViewBag.Departments,new { #class = "form-control" ,
#selected = ViewBag.Departments.Selected.HasValue && ViewBag.Departments.Selected.Value ? "selected" : null
})
#Html.ValidationMessageFor(model => model.DepartmentID, "", new { #class = "text-danger" })
</div>
</div>
Above, I have mentioned the code for the model, the controller and then the view.
Basically, I was trying to set a default DropDownList Element by taking user input from the database using the IsSelected field in the Department table.
So if the IsSelected Column in the Database for the corresponding department has a 1 and all the others have a NULL or a 0, then the department that has a 1 gets selected as the default element in the DropDownList when there is a Get.
But as soon as I run my code I am encountered with the error
(''System.Collections.Generic.List' does not contain a definition for 'Selected'')
Unable to figure out such behavior.
Thanks in Advance!!!
Remove ur #selected = html attribute in the #Html.DropDownListFor, the drop down is smart enough to set the selected from the SelectListItem
In my case i filled the viewBag with strings so i create the collection of SelectListItem in #Razer but can also be filled from the controller
this is working for me:
in the xxx.cshtml file
#{
var weights = new List<SelectListItem>();
foreach (var item in ViewBag.PossibleWeights)
{
weights.Add(
new SelectListItem
{
Text = item,
Value = item,
Selected = item == Model.WeightCode
});
}
}
#Html.DropDownListFor(model => model.WeightCode, weights, new { #class = "form-control")

Getting type instead of value

Trying to create a dropdownbox. I want to populate the list with values, but instead I get types in it.
ViewModel
public class AdminViewModel
{
...
public IEnumerable<SelectListItem> Roles { get; set; }
}
Controller
[HttpGet]
public ActionResult AddAdmin()
{
DataAccessLayer.DoloContext col = new DataAccessLayer.DoloContext();
List<Roles> list = new List<Roles>(col.Roles.ToList());
AdminViewModel viewMod = new AdminViewModel();
viewMod.Roles = new SelectList(list);
return View(viewMod);
}
View
<div class="form-group">
#Html.LabelFor(model => model.Roles, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Roles, (SelectList) Model.Roles, "RoleName", "Choose")
</div>
</div>
What I get in my dropdownlist is AuthSys.Models.Roles
What am I doing wrong?
I have tried compare to the examples I can find here, but in the end, I keep getting the types.
Try populating your IEnumerable<SelectListItem> as the following:
viewMod.Roles = col.Roles
.ToList()
.Select(x => new SelectListItem()
{
Value = x.Value,
Text = x.Text
}).ToList();
With x.Value and x.Text being your desired properties of Role.
Assumed that you have Roles class in data source like this:
public class Roles
{
public int RoleId { get; set; }
public string RoleName { get; set; }
}
If you want to get selected value from IEnumerable<SelectListItem>, do the following steps:
1) Create additional property which will hold selected value in viewmodel.
public class AdminViewModel
{
public int SelectedRoleId { get; set; }
public IEnumerable<SelectListItem> Roles { get; set; }
}
2) Bind the option list into IEnumerable<SelectListItem> property like this:
public ActionResult AddAdmin()
{
DataAccessLayer.DoloContext col = new DataAccessLayer.DoloContext();
var viewMod = new AdminViewModel();
List<Roles> list = col.Roles.ToList();
viewMod.Roles = list.Select(x => new SelectListItem {
Text = x.RoleName,
Value = x.RoleId
}).ToList();
return View(viewMod);
}
3) Finally, bind selected value property and option list to DropDownListFor helper, also no need to convert Roles option list into SelectList because Roles already has type of IEnumerable<SelectListItem>:
#Html.DropDownListFor(model => model.SelectedRoleId, Model.Roles, "Choose")

Checkboxlist MVC Partial

I have the following view model code:
public class TestCheckboxlistParentModel
{
public TestCheckboxlistParentModel()
{
CBL = new TestCheckboxlistModel();
}
public TestCheckboxlistModel CBL { get; set; }
}
public class TestCheckboxlistModel
{
public string TextField { get; set; }
public IList<string> SelectedFruits { get; set; }
public IList<SelectListItem> AvailableFruits { get; set; }
public TestCheckboxlistModel()
{
SelectedFruits = new List<string>();
AvailableFruits = new List<SelectListItem>();
}
}
controller:
public ActionResult TestCheckboxlist()
{
var model = new TestCheckboxlistParentModel
{
CBL = new TestCheckboxlistModel()
{
AvailableFruits = GetFruits()
}
};
return View(model);
}
[HttpPost]
public ActionResult TestCheckboxlist(TestCheckboxlistParentModel model)
{
if (ModelState.IsValid)
{
// Save data to database, and redirect to Success page.
return RedirectToAction("Success");
}
//model.AvailableFruits = GetFruits();
return View(model);
}
public ActionResult Success()
{
return View();
}
private IList<SelectListItem> GetFruits()
{
return new List<SelectListItem>
{
new SelectListItem {Text = "Apple", Value = "1"},
new SelectListItem {Text = "Pear", Value = "2"},
new SelectListItem {Text = "Banana", Value = "3"},
new SelectListItem {Text = "Orange", Value = "4"},
};
}
partial view:
#model Web.ViewModels.TestCheckboxlistModel
<div class="form-group">
#Html.LabelFor(model => model.TextField)
<div class="col-md-10">
#Html.EditorFor(model => model.TextField)
</div>
</div>
#foreach (var item in Model.AvailableFruits)
{
<div class="checkbox">
<label>
<input type="checkbox"
name="#Html.IdFor(p=>p.SelectedFruits)"
value="#item.Value" /> #item.Text
</label>
</div>
}
view:
#model Web.ViewModels.TestCheckboxlistParentModel
#{
ViewBag.Title = "TestCheckboxlist";
Layout = "~/Views/Shared/_LayoutApplicationDriver.cshtml";
}
#using (Html.BeginForm())
{
#Html.Partial("TestPartialCheckboxlist", Model.CBL, new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "CBL" } })
<div class="form-group text-center">
<input type="submit" class="btn btn-primary" value="Submit" />
</div>
}
Problem is SelectedFruits always does not have any elements in post method. The same code work correctly, if I don't use nested Partial view. Property TextField works fine with Partial
PS. It's not a dublicate of How to make Check Box List in ASP.Net MVC question. That question is a base of my answer. In my case, I need to have checkboxlist in partial view, where it does not work!
You use of name="#Html.IdFor(p => p.SelectedFruits)" generates name="CBL_SelectedFruits", but in order to bind to your model, you would need name="CBL.SelectedFruits" (note the . dot, not _ underscore) which you could generate using
name="#Html.NameFor(p => p.SelectedFruits)"
However there are other issues with your code. Your not strongly binding to your model, you get no validation, your generating a IList<SelectListItem> for property AvailableFruits when you don't need it (it could be just IList<string> AvailableFruits, and most importantly, if you return the view, all the checkboxes the user checked are lost (all checkboxes will be unchecked).
Change your view models so that you can strongly bind to your properties
public class FruitVM
{
public string Name { get; set; }
public bool IsSelected { get; set; }
}
public class ParentVM
{
public string TextField { get; set; }
public List<FruitVM> Fruits { get; set; }
}
and in the GET method
ParentVM model = new ParentVM
{
Fruits = new List<FruitVM>{
new FruitVM{ Name = "Apple" },
new FruitVM{ Name = "Pear" },
....
}
};
return View(model);
and create an EditorTemplate for FruitVM - in /Views/Shared/EditorTemplates/FruitVM.cshtml
#model FruitVM
#Html.CheckBoxFor(m => m.IsSelected)
#Html.LabelFor(m => m.IsSelected, Model.Name)
and in the view
#Html.ParentVM
....
#using (Html.BeginForm())
{
#Html.LabelFor(m => m.TextField)
#Html.EditorFor(m => m.TextField)
#Html.EditorFor(m => m.Fruits)
<input type="Submit" value="Save" />
}
The EditorFor() method will generate the correct html for each item in your collection.
Then in the POST method, you can get the selected items with
[HttpPost]
public ActionResult TestCheckboxlist(ParentVM model)
{
....
List<string> selectedFruits = model.Fruits.Where(x => x.IsSelected);

How can I pass list of strongly type objects from controller to a dropdown on a view?

I would like to pass list of strongly typed object to a dropdown which is located on my view.
Usually to achieve this I used ViewBags like in following example:
public ActionResult ChooseLevel()
{
List<Levels> LevelList = GetAllLevels();
ViewBag.LevelList = LevelList
var model = new Levels();
return View(model);
}
And I would simply write this on a view, and I would get all my levels listed there:
<div class="form-group">
#Html.LabelFor(model => model.LevelId, new { #class = "control-label col-md-3 col-sm-3" })
<div class="col-md-9 col-sm-9">
#Html.DropDownListFor(model => model.LevelId, new SelectList(ViewBag.LevelList, "LevelId", "LevelName"), "", new { #class = "form-control" })
</div>
</div>
But now I'm wondering can I simply pass my list of Levels there, and choose them from dropdown list, without storing them to a viewbag first?
For example :
public ActionResult ChooseLevel()
{
List<Levels> LevelList = GetAllLevels();
return View(LevelList);
}
On a view I would accept multiple items by writing IEnumerable on a view:
#model IEnumerable<Levels>
and after that I could somehow choose only one item and post it back to a server?
How can I solve that issue?
You need to add this List to your existing Model or View Model:
class ModelName
{
public virtual IEnumerable<SelectListItem> lstTypes { get; set; }
public virtual int intTypeId { get; set; }
//Other existing properties here
}
On your Controller, you can now add this list to your Model before you return to your view:
ModelName objModel = new ModelName();
List<Levels> LevelList = GetAllLevels();
objModel.lstTypes = LevelList.Select(y => new SelectListItem()
{
Value = y.LevelId.ToString(),
Text = y.LevelName.ToString()
});
return View(objModel);
Then you can now display it on your view:
#model ModelName
//First parameter will be the Id that will be selected by your user when they post it
//Second parameter will be the enumerable list of dropdown
//Third parameter is the default option which is optional, and the last is the HTML attributes
#Html.DropDownListFor(c => c.intTypeId, Model.lstTypes , "Please select an item", new { #class = "form-control" })
You can create new viewmodel that contains multiple models (old model and LevelList model). like this:
public class newViewModel
{
public IEnumerable<level> levels{ get; set;}
public OldModel oldModel {get; set;}
}
Model class
public class TestViewModel
{
public List<SelectListItem> EnterpriseList { get; set; }
}
Controller:
var model = new TestViewModel() {
EnterpriseList = EnterpriseData.Select(p=>new SelectListItem() { Value = p.Value,Text = p.Name}).ToList()
};
return View(model);
View:
#Html.DropDownListFor(p => p.Enterprise, Model.EnterpriseList, "Please select a", new { #class = "form-control", #style = "height: auto" })

ICollection<SelectListItem> in MVC ViewModel

I've got a viewmodel for a page where fields are combined into fieldsets.
The VM looks like this:
public class FieldsetVM
{
public Int64 ID { get; set; }
public string Name { get; set; }
[Display(Name = "Available Fields")]
public ICollection<SelectListItem> AvailableFields { get; set; }
[Display(Name = "Current Fields")]
public ICollection<SelectListItem> UsedFields { get; set; }
public FieldsetVM(int id, string name, List<Field> availFields, List<Field> usedFields)
{
this.ID = id;
this.Name = name;
this.AvailableFields = new List<SelectListItem>();
foreach (Field field in availFields)
this.AvailableFields.Add(new SelectListItem { Text = string.Format("{0} ({1})", field.Name, field.FieldType.ToString()), Value = field.FieldID.ToString() });
this.UsedFields = new List<SelectListItem>();
foreach (Field field in usedFields)
this.UsedFields.Add(new SelectListItem { Text = string.Format("{0} ({1})", field.Name, field.FieldType.ToString()), Value = field.FieldID.ToString() });
}
public FieldsetVM()
{
}
}
Get in the controller looks like this:
[HttpGet]
public ActionResult Create()
{
FieldsetVM vm = new FieldsetVM(0, "", uw.FieldRepo.Get().ToList(), new List<Field>());
return View(vm);
}
Relevant piece of the view looks like this:
<div class="col-md-3 col-xs-6">
<div class="editor-label">
#Html.LabelFor(m => m.AvailableFields)
</div>
<div class="editor-field">
#Html.ListBoxFor(m => m.AvailableFields, Model.AvailableFields)
</div>
<button type="button" onclick="moveSelected('AvailableFields','UsedFields');">Move Selected</button>
</div>
<div class="col-md-3 col-xs-6">
<div class="editor-label">
#Html.LabelFor(m => m.UsedFields)
</div>
<div class="editor-field">
#Html.ListBoxFor(m => m.UsedFields, Model.UsedFields)
</div>
<button type="button" onclick="moveSelected('UsedFields','AvailableFields');">Remove Selected</button>
</div>
A tiny bit of JavaScript wires up the two listboxes:
function moveSelected(firstSelectId, secondSelectId) {
$('#' + firstSelectId + ' option:selected').appendTo('#' + secondSelectId);
$('#' + firstSelectId + ' option:selected').remove();
}
And then I have a POST in the controller:
[HttpPost]
public ActionResult Create(FieldsetVM postedVm)
{
Fieldset fs = new Fieldset();
fs.Name = postedVm.Name;
if (fs.Fields == null)
fs.Fields = new List<Field>();
fs.Fields.Clear();
foreach (SelectListItem item in postedVm.UsedFields)
fs.Fields.Add(uw.FieldRepo.GetByID(item.Value));
uw.FieldsetRepo.Insert(fs);
return RedirectToAction("Index");
}
My expectation is that in the postedVm, we would be able to see the values the user selected into UsedFields. Instead, UsedFields and AvailableFields are ALWAYS blank when the user posts back to the HttpPost Create() action.
I'm trying to figure out why: Surely moving items between list boxes is a fairly common way to configure things? Shouldn't MVC take a look at the values in the generated and use them to populate the postedVm object?
EDIT Based on feedback from best answer, here is my revised Create/Post action.
[HttpPost]
public ActionResult Create(FieldsetVM postedVm, int[] UsedFields)
{
Fieldset fs = new Fieldset();
fs.Name = postedVm.Name;
fs.Fields = new List<Field>();
foreach (int id in UsedFields)
fs.Fields.Add(uw.FieldRepo.GetByID(id));
uw.FieldsetRepo.Insert(fs);
uw.Save();
return RedirectToAction("Index");
}
When you post the form, only the Ids for AvailableFields and UsedFields will be posted. If you have multiple values, then you'll get a comma seperated list of ids, so modelbinding will not be able to bind those posted Ids to FieldsetVM postedVm.
If you do something like public ActionResult Create(int[] availableFields, int[] usedFields) you should be able to get the selected Ids.

Categories

Resources