i've a view. in the view i've months field(nvarchar type in database) :
#Html.DropDownListFor(model => model.rent_month,
(IEnumerable<SelectListItem>)ViewBag.months)
i've a method in a model class (PostManager) to generate months list like:
public IEnumerable<SelectListItem> GetMyMonthList()
{
return CultureInfo.CurrentCulture.DateTimeFormat.MonthNames
.Select(m => new SelectListItem() { Text = m, Value = m });
}
i get months in get action by :
public ActionResult Create()
{
PostModel p = new PostModel();
ViewBag.months = pm.GetMyMonthList();
return View(p);
}
in my Model my month attributes:
[Required(ErrorMessage = "You Must Select a Month.")]
[Display(Name = "Select Rent Month")]
public string rent_month { get; set; }
in the post action:
public ActionResult Create(PostModel p)
{
if (ModelState.IsValid)
{
post post = new Models.DB.post();
post.rent_month = p.rent_month;
db.posts.AddObject(post);
db.SaveChanges();
}
}
it generates month in the dropdownlist correctly.But after submit the form it gives error:
The ViewData item that has the key 'rent_month' is of type 'System.String' but must be of type 'IEnumerable'
now what is the solution for this error... thanks in advance...
I believe this is happening because in your post action you are not populating the ViewBag again. Make sure you set ViewBag.months = pm.GetMyMonthList(); in your controller POST action similar to what you have done in GET action.
Better solution would be to have a IEnumerable<SelectListItem> MonthList property as part of the PostModel. Instead of loading the months from ViewBag you can access it directly by the MonthList property
In the PostModel
public IEnumerable<SelectListItem> MonthList
{
get
{
return pm
.GetMonthList()
.Select(a => new SelectListItem
{
Value = a.Id,
Text = a.MonthText
})
.ToList();
}
}
Then in the view
#Html.DropDownListFor(model => model.rent_month, Model.MonthList)
After EDIT to the question
Your PostModel class should be like this. I have moved your GetMyMonthList() implementation out of the PostManager class.
public class PostModel
{
[Required(ErrorMessage = "You Must Select a Month.")]
[Display(Name = "Select Rent Month")]
public string rent_month { get; set; }
public IEnumerable<SelectListItem> MonthList
{
get
{
return CultureInfo.CurrentCulture.DateTimeFormat.MonthNames
.Select(m => new SelectListItem() { Text = m, Value = m });
}
}
}
public class PostModel
{
[Required(ErrorMessage = "You Must Select a Month.")]
[Display(Name = "Select Rent Month")]
public string rent_month { get; set; }
public IEnumerable<SelectListItem> MonthList
{
get
{
return CultureInfo.CurrentCulture.DateTimeFormat.MonthNames
.Select(m => new SelectListItem() { Text = m, Value = m });
}
}
}
Related
I'm busy with an ASP.NET Core MVC application, and I'm trying to populate a drop down list. I've created a view model and I have added a method to my StoresController that returns a list of stores that I want to display in a dropdown. I've been working off some online tutorials as I'm very new to asp.
View model:
public class StoreListViewModel
{
public List<StoreList> StoreList { get; set; } = new List<StoreList>();
}
public class StoreList
{
public string StoreId { get; set; } = null!;
public string StoreName { get; set; } = null!;
}
StoresController:
public IActionResult LoadStoreList()
{
if (ModelState.IsValid)
{
var storeList = new StoreListViewModel().StoreList.Select
(x => new SelectListItem { Value = x.StoreId, Text = x.StoreName }).ToList();
ViewBag.Stores = storeList;
}
return NotFound();
}
I'm trying to use ViewBag to call my LoadStoreList() method.
<select name="storeList" class="form-control" asp-items="#(new SelectList(ViewBag.Stores, "Value", "Text"))"></select>
When I load my page I get the following error
Value cannot be null. (Parameter 'items')
The page I need the dropdown list on is my CreateUser.cshtml which is bound to my UserModel and has a UsersController. The method I have created for listing the stores is in my StoresController which is bound to my StoresModel. So I'm not sure if that's causing the issue.
I've been battling with this for days, if someone could help me get this working or show me a better method, that would be great.
*Edit
The UserIndex() method is the first method that fires when my users page opens, do I call the LoadStoreList() method from there ?
UserController
public async Task<IActionResult> UsersIndex()
{
return _context.UsersView != null ?
View(await _context.UsersView.ToListAsync()) :
Problem("Entity set 'ApplicationDbContext.Users' is null.");
}
I'm trying to use ViewBag to call my LoadStoreList() method.
ViewBag cannot be used to call any method. You just need set value for ViewBag in the method which renders your show dropdownlist's page.
From your description, you said the page you need the dropdown list on is CreateUser.cshtml. Assume that you render the CreateUser.cshtml page by using CreateUser action.
CreateUser.cshtml:
<select name="storeList" class="form-control" asp-items="#(new SelectList(ViewBag.Stores, "Value", "Text"))"></select>
Controller:
public class YourController : Controller
{
private readonly YourDbcontext _context;
public YourController(YourDbcontext context)
{
_context = context;
}
[HttpGet]
public IActionResult CreateUser()
{
var storeList = _context.StoreLists.Select
(x => new SelectListItem { Value = x.StoreId , Text = x.StoreName }).ToList();
ViewBag.Stores = storeList;
return View();
}
}
YourDbcontext should be something like:
public class YourDbcontext: DbContext
{
public YourDbcontext(DbContextOptions<MvcProjContext> options)
: base(options)
{
}
public DbSet<StoreList> StoreLists{ get; set; }
}
Dont use viewbag for storing list data. Make your view page model including List, for example:
public class UserCreationViewModel{
public int Id{ get; set; }
public string Name { get; set; }
// Any other properties....
public List<StoreList> StoreList { get; set; }
}
in your controller YourController:
[HttpGet]
public IActionResult CreateUser()
{
var storeList = new StoreListViewModel().StoreList.Select
(x => new SelectListItem { Value = x.StoreId, Text = x.StoreName }).ToList();
UserCreationViewModel model=new UserCreationViewModel{
StoreList = storeList
};
return View("createUserViewName", model);
}
in createUserViewName:
#Html.DropDownList("StoreId", new SelectList(Model.StoreList, "StoreId", "StoreName"), "Select", new { #class = "form-control" })
or
<select class="form-control" asp-for="#Model.StoreId" asp-items="#(new SelectList(Model.StoreList, "StoreId", "StoreName"))">
<option value="-1">Select</option>
</select>
I'm making a strongly typed update/create view for "Medical Supplies" in which users can select multiple options ("Kits") from a dropdown list that received a List<SelectListItem>. The list is a <select multiple="multiple">. It was originally working perfectly, but I must have accidentally changed something.
Now the dropdown does not display SelectListItems passed to it as selected = true as selected (as verified by VS debugger), so I can select new items but not deselect previously selected ones. I need this to compare the list of IDs from the new selection to the old one in order to determine what must be removed from the Db.
This is my view model:
public class MedicalSupplyViewModel
{
public MedicalSupplyViewModel()
{
Supply = new MedicalSupply();
KitList = new List<SelectListItem>();
KitIds = new List<int>();
}
public MedicalSupply Supply { get; set; }
public List<SelectListItem> KitList { get; set; }
public string StringKits { get; set; }
public List<int> KitIds { get; set; }
public void KitStringSet()
{
IEnumerable<string> KitNames = Supply.KitSupplies.Select(ks => ks.Kit.Name);
StringKits = Supply.KitSupplies.Count() == 0 ? "N/A" : string.Join(", " , KitNames);
}
}
This is the relevant cshtml in my view:
<select multiple="multiple" class="form-control" id="kit_select" asp-for="KitIds" asp-items="Model.KitList"></select>
This is the part of the controller method for this page that creates the SelectListItems:
DetailsModel.KitList = _db.Kits.ToList().ConvertAll(k =>
{
return new SelectListItem()
{
Value = k.Id.ToString(),
Text = k.Name,
Selected = SelectedKits.Contains(k)
};
});
Even setting the item to selected = true will not display them as such. I've set breakpoint everywhere, including in the view, and I cannot find a discrepancy between the selected property and what it should be anywhere except for the rendered html. I've also used different browsers and spent hours searching the internet and this website.
What could be the cause of this issue?
You should set the selected items' value to the KitIds. Below is my test example:
Model:
public class MedicalSupplyViewModel
{
public MedicalSupplyViewModel()
{
KitList = new List<SelectListItem>();
KitIds = new List<int>();
}
public List<SelectListItem> KitList { get; set; }
public string StringKits { get; set; }
public List<int> KitIds { get; set; }
}
public class Kit
{
public int Id { get; set; }
public string Name { get; set; }
}
Controller:
public IActionResult Index()
{
List<Kit> kits = new List<Kit>
{
new Kit{ Id = 1, Name = "AA"},
new Kit{ Id = 2, Name = "BB"},
new Kit{ Id = 3, Name = "CC"},
};
List<Kit> SelectedKits = new List<Kit>
{
new Kit{ Id = 1, Name = "AA"},
new Kit{ Id = 2, Name = "BB"}
};
var DetailsModel = new MedicalSupplyViewModel();
DetailsModel.KitIds = SelectedKits.Select(x => x.Id).ToList();
DetailsModel.KitList = kits.ToList().ConvertAll(k =>
{
return new SelectListItem()
{
Value = k.Id.ToString(),
Text = k.Name
};
});
return View(DetailsModel);
}
View:
<select multiple="multiple" class="form-control" id="kit_select" asp-for="KitIds" asp-items="Model.KitList"></select>
Result:
I create a #Html.DropDownListFor and populate it from the database. How can I set a selected value to the drop down?
My View:
#Html.DropDownListFor(m => m.Forms, new SelectList(Model.Forms, "FormsCreatorID", "FormName"),
"Select a Form", new { #class = "form-control" })
My Controller:
var forms = db.formscreators.Where(fp => fp.PropertyID == id || fp.PropertyID == 0)
.OrderByDescending(x => x.PropertyID).GroupBy(x => x.FormName).Select(x => x.FirstOrDefault()).ToList();
var viewModel = new ListFormsCreator { Forms = forms };
My ViewModel:
public class ListFormsCreator
{
public List<formscreator> Forms { get; set; }
}
My Database Model:
public partial class formscreator
{
public int FormsCreatorID { get; set; }
public string FormName { get; set; }
public int PropertyID { get; set; }
}
Thanks
You should add another property to your view model for the store/pass the selected option.
public class ListFormsCreator
{
public int SelectedFormId { set;get;}
public List<formscreator> Forms { get; set; }
}
Now in your GET action, you can set that value
var viewModel = new ListFormsCreator() { Forms = forms };
viewModel.SelectedFormId = 2 ; // This will select the option with 2 as FormsCreatorID
return View(viewModel);
And in the view use the lambda expression with that property as the first parameter of the DropDownListFor helper method.
#model ListFormsCreator
#Html.DropDownListFor(m => m.SelectedFormId ,
new SelectList(Model.Forms, "FormsCreatorID", "FormName"),
"Select a Form", new { #class = "form-control" })
The DropDownListFor helper method will use the value of SelectedFormId property and select the option which has the same value attribute value from the list of options of that SELECT element.
You can also remove the dependency on formscreator class from the view model, by replacing it with a list of SelectListItem
public class ListFormsCreator
{
public int SelectedFormId { set;get;}
public List<SelectListItem> Forms { get; set; }
}
Now in your GET action, you can use the Select method to generate the lsit of SelectListItem from your other collection.
var viewModel = new ListFormsCreator();
viewModel.Forms = someCollection.Select(a=>new SelectListItem {
Value=a.FormsCreatorId.ToString(),
Text=a.FormName})
.ToList();
viewModel.SelectedFormId = 2 ; // This will select the option with 2 as FormsCreatorID
return View(viewModel);
Assuming someCollection is a collection of formscreator objects
Now in the view code is much simpler
#Html.DropDownListFor(m => m.SelectedFormId, Model.Forms ,"Select a Form")
Conform with C#/.NET naming conventions:
Rename formscreator to FormsCreator
Replace ID with Id (as it's an abbreviation, not an initialism)
Rename ListFormsCreator to something like ListFormsCreatorViewModel so it's obvious it's a ViewModel type and not a Model/Entity type.
Modify your ViewModel to add a property to store the selected FormsCreatorId value:
public class ListFormsCreatorViewModel
{
[Required] // add or remove the 'Required' attribute as necessary
public int? SelectedFormsCreatorId { get; set; }
...
}
Set the SelectedFormsCreatorId property value in your controller action if necessary if you know what the value should be.
In your POST handler, ensure the SelectedFormsCreatorId value is maintained, either by directly passing-through the model action parameter back through the View(Object viewModel) method or manually repopulating it.
The view-model property in DropDownListFor should be the SelectedFormsCreatorId property. You do not need new SelectList(...)
#Html.DropDownListFor( m => m.SelectedFormsCreatorId, this.Model.Forms );
Update your viewModel and add an Int SelectId for the dropdown selected value.
In your controller:
var viewModel = new ListFormsCreator { SelectId = PropertyId, Forms = FormSelectList(forms, PropertyId.ToString()) };
I would create a function passing in a list:
public static SelectList FormSelectList(IEnumerable<formscreators> types, string selected = null)
{
return new SelectList(from f in forms
select new SelectListItem
{
Text = f.FormName,
Value = f.FormsCreatorID.ToString()
}, "Value", "Text", selected);
}
And in your .cshtml
#Html.DropDownListFor(m => m.PropertyId, Model.forms, "Select a Form", new { #class = "form-control", required = "required" })
You should generate a 'SelectListItem' list on the controller with setting 'Selected' value and pass it via ViewBag or ViewModel. In my sample, for simplicity, I used ViewBag.
Here is the shortened Controller:
public ActionResult Edit(int? id)
{
if (id == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
Album album = context.Albums.Find(id);
if (album == null)
{
return HttpNotFound();
}
ViewBag.GenreId = context.Genres.Select(
g => new SelectListItem()
{
Value = g.GenreId.ToString(),
Text = g.Name,
Selected = g.GenreId == album.GenreId ? true : false
}).ToList();
return View(album);
}
Here is the shortened View Code
#using MvcMusicStore2017.Models;
#model Album
#Html.DropDownList("GenreId", null, new { #class = "form-control" })
I am in a big trouble. I read 4 stackoverflow question and one blogpost. I have tried 5 different approach to view the selected items in a multiple selectlist.
I have no success.
The multiple selectlist is generated, but it does not select the items. I have no more idea.
Model:
public class EditableModel
{
public IList<Company> SelectedCompanies { get; set; }
public IList<SelectListItem> SelectListCompanies { get; set; }
}
Controller:
public ActionResult Edit(int id)
{
var service = _serviceDAL.GetEditableModel(id);
if (service!= null)
{
service.SelectListCompanies = GetSelectListCompanies(service.SelectedCompanies);
return View(service);
}
}
private IList<SelectListItem> GetSelectListCompanies(IList<Company> selectedCompanies)
{
List<SelectListItem> items = new List<SelectListItem>();
foreach (Companycompany in _companyService.GetCompanies())
{
items.Add(new SelectListItem
{
Value = company.CompanyId.ToString(),
Text = company.Name,
Selected = selectedCompanies.Any(x => x.CompanyId == company.CompanyId)
});
}
return items;
}
View
#Html.ListBox("SelectedCompanies", Model.SelectListCompanies, Model.SelectedCompanies.Select(x => x.CompanyId.ToString()) )
And nothing. The items in the select list is not selected...
I have tried this Multiselect, the same result, or this one as the current solution.
You cannot bind a <select multiple> to a collection of complex objects. It binds to, and posts back an array of simple values (the values of the selected options).
Your SelectedCompanies property needs to be IEnumerable<int> (assuming the CompanyId of Company is also int). Note also the Selected property of SelectListItem is ignored when binding to a property.
Your also using the same collection for the selected Companies and the list of all Companies which makes no sense. Your SelectListCompanies should be generated from your table of Company.
Model
public class MyViewModel
{
public IEnumerable<int> SelectedCompanies { get; set; }
public IEnumerable<SelectListItem> SelectListCompanies { get; set; }
}
Base on your current code for EditableModel, your code should be
public ActionResult Edit(int id)
{
var service = _serviceDAL.GetEditableModel(id);
....
MyViewModel model = new MyViewModel
{
SelectedCompanies = service.SelectedCompanies.Select(x => x.CompanyId),
SelectListCompanies = GetSelectListCompanies()
};
return View(model);
private IEnumerable<SelectListItem> GetSelectListCompanies()
{
var all companies = ... // call method to get all Companies
return companies.Select(x => new SelectListItem
{
Value = x.CompanyId.ToString(),
Text = x.Name
});
}
However, it look like you should be modifying your EditableModel and the GetEditableModel() code to return the correct data in the first place.
I managed to populate DropDownList with value from a Database in ASP.NET MVC 5. My goal is to assing one of the dropDownList's value to a specific model, and send it back to the Database. So, if i leave the default value in the dropdownlist, the data in SQL server is null, which is Okay, but if I choose an option, I get an error :
Exception thrown: 'System.InvalidOperationException' in System.Web.Mvc.dll ("There is no ViewData item of type 'IEnumerable' that has the key 'Status'."). I tried everything so far and i am opened for suggestions. Thank you !!!
In Controller :
ViewBag.Status = new SelectList(db.Status, "Id", "Name");
in View
#Html.DropDownList("Status","Select status...")
In Controller so far..
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Apply(ViewModelVM vm,int x=0)
{
myDb db = new myDb();
ViewBag.SocialStatus = new SelectList(db.SocialStatuses, "Id", "StatusDescription");
return View();
}
[HttpPost]
public ActionResult Apply(ViewModelVM vm)
{
if (ModelState.IsValid)
{
using (myDb db = new myDb())
{
var personalinfo = new PersonalInformation()
{
FirstName = vm.PersonalInformation.FirstName,
LastName = vm.PersonalInformation.LastName,
Birthdate = vm.PersonalInformation.Birthdate,
SocialStatus = vm.SocialStatus
};
ViewBag.SocialStatus = new SelectList(db.SocialStatuses, "Id", "StatusDescription");
db.PersonalInformations.Add(personalinfo);
db.SaveChanges();
}
return View("Success");
}
return View();
}
The model:
public partial class Status
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public SocialStatus()
{
PersonalInformations = new HashSet<PersonalInformation>();
}
public int Id { get; set; }
[StringLength(20)]
public string StatusDescription { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<PersonalInformation> PersonalInformations { get; set; }
}
}
The ViewModel:
public class ViewModelVM
{
...
public Status SocialStatus { set; get; }
...
}
Firstly your using a view model so include a property in your view model for the SelectList
public IEnumerable<SelectListItem> StatusList { get; set; }
Next remove the parameter for the model from the GET method (and since you don't appear to be using the value of x, that should be removed also)
[HttpGet]
public ActionResult Apply(ViewModelVM vm,int x=0)
{
myDb db = new myDb();
ViewModelVM model = new ViewModelVM()
{
StatusList = new SelectList(db.SocialStatuses, "Id", "StatusDescription");
};
return View(model); // return the model to the view
}
Next, your dropdown is binding to a property named Status but your view model does not contain a property named status (its SocialStatus) and SocialStatus is a complex object and you cannot bind a <select> to a complex object (a <select> only posts back a single value (or array or values in the case of <select multiple>).
In addition, because your view model contains a property which is a complex object with validation attributes on its properties, ModelState will always be invalid because you do not post back a value for StatusDescription. As a result you always return the view in the POST method, and because you have not reassigned ViewBag.Status = ...., it is null, hence the error.
Remove property public Status SocialStatus { set; get; } and include
[Display(Name = "Social Status")]
[Required(ErrorMessage = "Please select a status")]
public int SocialStatus { get; set; }
an then in the view, strongly bind to your model using
#Html.LabelFor(m => m.SocialStatus)
#Html.DropDownListFor(m => m.SocialStatus, Model.StatusList, "-Please select-")
#Html.ValidationMessageFor(m => m.SocialStatus)
Then, in the POST method, if ModelState is invalid, populate the select list again before returning the view
if(!ModelState.IsValid)
{
model.StatusList = new SelectList(db.SocialStatuses, "Id", "StatusDescription");
return View(model);
}
// save and redirect
Finally, review What is ViewModel in MVC?.