I am having a problem passing to the View a Model that contains the dropdown data and the model.
With this code my page loads, but the dropdownlist contains "System.Web.MVC.SelectList" when selected.
Here's my controller code.
public ActionResult Index(string productNameFilter, string productCategoryFilter, String productTypeFilter )
{
var ddl = new Items();
ddl.CategoryddList = itemsRepository.GetItemDdl("Item Categories").Select(c => new SelectListItem
{
Value = c.DropdownID.ToString(),
Text = c.DropdownText
});
ViewBag.CategoryDD = new SelectList(ddl.CategoryddList, "Value", "Text");
var model = itemsRepository.GetItemByName(productNameFilter);
return View(model);
}
Here's my view
#model Ienumerable<Models.items.items>
#Html.DropDownList("productCategoryFilter",
new SelectList(ViewBag.CategoryDD),
"---Select Category---")
Side note - if you use a ViewModel between the View and the Model instead of binding directly to the model, you can put your SelectList on the ViewModel and use #Html.DropdownFor() instead of #Html.Dropdown(). The ViewBag should really be used sparingly.
However back to your original question:
What is "Items()"? in your line
var ddl = new Items();
I'm not sure what good reason you would have NOT to make it enumerable.
I suspect it is not working because you are making a selectlist from a select list twice --
in your code behind you are defining ViewBag.CategoryDD as a SelectList(), and then in your Razor code you are creating a new SelectList() from the existing selectlist. You shouldn't have to do this.
The way I would do this is create a ProductViewModel class that contains your product category list AND your list of products (your current model), and a property for the selected filter.
public class ProductViewModel
{
public IEnumerable<Model.items.items> ProductList {get;set;}
public IEnumerable<SelectListItem> ProductCategoryList {get;set;} //SelectList is an IEnumerable<SelectListItem>
public string SelectedCategory {get;set;}
}
Then on your view the model would be
#model ProductViewModel
#Html.DisplayFor(model => model.SelectedCategory, "---Select Category---")
#Html.DropdownListFor(model => model.SelectedCategory, Model.ProductCatgoryList)
Related
This question and community wiki answer has been added to assist in closing out numerous unanswered questions as discussed in this meta post.
I have some code and when it executes, it throws an exception saying:
The model item passed into the dictionary is of type Bar but this dictionary requires a model item of type Foo
What does this mean, and how do I fix it?
The error means that you're navigating to a view whose model is declared as typeof Foo (by using #model Foo), but you actually passed it a model which is typeof Bar (note the term dictionary is used because a model is passed to the view via a ViewDataDictionary).
The error can be caused by
Passing the wrong model from a controller method to a view (or partial view)
Common examples include using a query that creates an anonymous object (or collection of anonymous objects) and passing it to the view
var model = db.Foos.Select(x => new
{
ID = x.ID,
Name = x.Name
};
return View(model); // passes an anonymous object to a view declared with #model Foo
or passing a collection of objects to a view that expect a single object
var model = db.Foos.Where(x => x.ID == id);
return View(model); // passes IEnumerable<Foo> to a view declared with #model Foo
The error can be easily identified at compile time by explicitly declaring the model type in the controller to match the model in the view rather than using var.
Passing the wrong model from a view to a partial view
Given the following model
public class Foo
{
public Bar MyBar { get; set; }
}
and a main view declared with #model Foo and a partial view declared with #model Bar, then
Foo model = db.Foos.Where(x => x.ID == id).Include(x => x.Bar).FirstOrDefault();
return View(model);
will return the correct model to the main view. However the exception will be thrown if the view includes
#Html.Partial("_Bar") // or #{ Html.RenderPartial("_Bar"); }
By default, the model passed to the partial view is the model declared in the main view and you need to use
#Html.Partial("_Bar", Model.MyBar) // or #{ Html.RenderPartial("_Bar", Model.MyBar); }
to pass the instance of Bar to the partial view. Note also that if the value of MyBar is null (has not been initialized), then by default Foo will be passed to the partial, in which case, it needs to be
#Html.Partial("_Bar", new Bar())
Declaring a model in a layout
If a layout file includes a model declaration, then all views that use that layout must declare the same model, or a model that derives from that model.
If you want to include the html for a separate model in a Layout, then in the Layout, use #Html.Action(...) to call a [ChildActionOnly] method initializes that model and returns a partial view for it.
This question already has a great answer, but I ran into the same error, in a different scenario: displaying a List in an EditorTemplate.
I have a model like this:
public class Foo
{
public string FooName { get; set; }
public List<Bar> Bars { get; set; }
}
public class Bar
{
public string BarName { get; set; }
}
And this is my main view:
#model Foo
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
#Html.EditorFor(m => m.Bars)
And this is my Bar EditorTemplate (Bar.cshtml)
#model List<Bar>
<div class="some-style">
#foreach (var item in Model)
{
<label>#item.BarName</label>
}
</div>
And I got this error:
The model item passed into the dictionary is of type 'Bar', but this
dictionary requires a model item of type
'System.Collections.Generic.List`1[Bar]
The reason for this error is that EditorFor already iterates the List for you, so if you pass a collection to it, it would display the editor template once for each item in the collection.
This is how I fixed this problem:
Brought the styles outside of the editor template, and into the main view:
#model Foo
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
<div class="some-style">
#Html.EditorFor(m => m.Bars)
</div>
And changed the EditorTemplate (Bar.cshtml) to this:
#model Bar
<label>#Model.BarName</label>
Observe if the view has the model required:
View
#model IEnumerable<WFAccess.Models.ViewModels.SiteViewModel>
<div class="row">
<table class="table table-striped table-hover table-width-custom">
<thead>
<tr>
....
Controller
[HttpGet]
public ActionResult ListItems()
{
SiteStore site = new SiteStore();
site.GetSites();
IEnumerable<SiteViewModel> sites =
site.SitesList.Select(s => new SiteViewModel
{
Id = s.Id,
Type = s.Type
});
return PartialView("_ListItems", sites);
}
In my case I Use a partial view but runs in normal views
Consider the partial map.cshtml at Partials/Map.cshtml. This can be called from the Page where the partial is to be rendered, simply by using the <partial> tag:
<partial name="Partials/Map" model="new Pages.Partials.MapModel()" />
This is one of the easiest methods I encountered (although I am using razor pages, I am sure same is for MVC too)
First you need to return an IEnumerable version of your model to the list view.
#model IEnumerable<IdentityManager.Models.MerchantDetail>
Second, you need to return a list from the database. I am doing it via SQL Server, so this is code I got working.
public IActionResult Merchant_Boarding_List()
List<MerchantDetail> merchList = new List<MerchantDetail>();
var model = new MerchantDetail();
try
{
using (var con = new SqlConnection(Common.DB_CONNECTION_STRING_BOARDING))
{
con.Open();
using (var command = new SqlCommand("select * from MerchantDetail md where md.UserGUID = '" + UserGUID + "'", con))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
model.biz_dbaBusinessName = reader["biz_dbaBusinessName"].ToString();
merchList.Add(model);
}
}
}
}
}
catch (Exception ex)
{
}
return View(merchList);
Passing the model value that is populated from a controller method to a view
public async Task<IActionResult> Index()
{
//Getting Data from Database
var model= await _context.GetData();
//Selecting Populated Data from the Model and passing to view
return View(model.Value);
}
one more thing.
if your view is a partial/sub page and the model for that partial view is null for some reason (e.g no data) you will get this error. Just need to handle the null partial view model
Ok, I have read a bunch of articles, and I am still lost, so I figure I will put the question out here.
I am trying to create a dynamic dropdown in my "posts" create view. I would like to pull the selectList items from my Categories.sdf, which has a table called categories and two columns, "CategoryID" and "CategoryTitle".
I know I need to pull the items into the viewbag within by "postscontroller" so they can be passed to the view. But I am not sure what this would look like. Again, i'm new to MVC so if i sound like a dope, i apologize.
I know I need to pull the items into the viewbag within by "postscontroller"
Oh no, you don't need to do anything like that.
You could start by defining a view model:
public class PostViewModel
{
[DisplayName("Select a category")]
[Required]
public string SelectedCategoryId { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
that you will populate in your controller:
public class PostsController: Controller
{
public ActionResult Index()
{
var model = new PostViewModel();
model.Categories = db.Categories.ToList().Select(c => new SelectListItem
{
Value = c.CategoryId,
Text = c.CategoryName
});
return View(model);
}
}
and then have a corresponding strongly typed view (~/views/posts/index.cshtml):
#model PostViewModel
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.SelectedCategoryId)
#Html.DropDownListFor(x => x.SelectedCategoryId, Model.Categories, "-- select --")
#Html.ValidationMessageFor(x => x.SelectedCategoryId)
<button type="submit">OK</button>
}
I have a constants values such as "Required","Optional", and "Hidden". I want this to bind in the dropdownlist. So far on what I've done is the below code, this is coded in the view. What is the best way to bind the constant values to the dropdownlist? I want to implement this in the controller and call it in the view.
#{
var dropdownList = new List<KeyValuePair<int, string>> { new KeyValuePair<int, string>(0, "Required"), new KeyValuePair<int, string>(1, "Optional"), new KeyValuePair<int, string>(2, "Hidden") };
var selectList = new SelectList(dropdownList, "key", "value", 0);
}
Bind the selectList in the Dropdownlist
#Html.DropDownListFor(model => model.EM_ReqTitle, selectList)
Judging by the property EM_RegTitle I'm guessing that the model you're using is auto-generated from a database in some way. Maybe Entity Framework? If this is the case, then you should be able to create a partial class in the same namespace as your ORM/Entity Framework entities and add extra properties. Something like:
public partial class MyModel
{
public SelectList MyConstantValues { get; set; }
}
You can then pass your SelectList with the rest of the model.
There are usually hangups from using ORM/EF entities through every layer in your MVC app and although it looks easy in code examples online, I would recommend creating your own View Model classes and using something like AutoMapper to fill these views. This way you're only passing the data that the views need and you avoid passing the DB row, which could contain other sensitive information that you do not want the user to view or change.
You can also move the logic to generate your static value Select Lists into your domain model, or into a service class to help keep reduce the amount of code and clutter in the controllers.
Hope this helps you in some way!
Example...
Your View Model (put this in your "Model" dir):
public class MyViewModel
{
public SelectList RegTitleSelectList { get; set; }
public int RegTitle { get; set; }
}
Your Controller (goes in the "Controllers" dir):
public class SimpleController : Controller
{
MyViewModel model = new MyViewModel();
model.RegTitle = myEfModelLoadedFromTheDb.EM_RegTitle;
model.RegTitleSelectList = // Code goes here to populate the select list.
return View(model);
}
Now right click the SimpleController class name in your editor and select "Add View...".
Create a new view, tick strongly typed and select your MyViewModel class as the model class.
Now edit the view and do something similar to what you were doing earlier in your code. You'll notice there should now be a #model line at the top of your view. This indicates that your view is a strongly typed view and uses the MyViewModel model.
If you get stuck, there are plenty of examples online to getting to basics with MVC and Strongly Typed Views.
You would prefer view model and populate it with data in controller.
class MyViewModel
{
public string ReqTitle { get; set; }
public SelectList SelectListItems { get; set; }
}
Then you can use:
#Html.DropDownListFor(model => model.EM_ReqTitle, model.SelectListItems)
I have a situation where I am wanting an 'Add' View to accept an int from the controller but return a different type to the HttpPost controller method. Confusing, I know. The 'Add' View is used to create an object typed as a Widget, but I need to pass in the ID of the WidgetCategory. So in my WidgetController, I would have a method something like:
public ActionResult Add(int id) // 'id' is the WidgetCategoryID
{
return View(id);
}
However, in the view, since in it's intended to return a Widget to be added would start like this:
#using MyProject.Models
#model Widget
#{
ViewBag.Title = "My Title";
Layout = "~/Views/Shared/_MyLayout.cshtml";
}
#using (Html.BeginForm())
{
// Insert markup here ...
}
My question is, how do I pass the WidgetCategoryID into the controller if it's typed to return a Widget? I was hoping to add it as a hidden field inside the form like so:
#Html.Hidden(id)
Any ideas would be greatly appreciated.
Thanks!
Assuming your ViewModel Widget has a WidgetCategoryId property
public class Widget
{
public int ID { set;get;}
public int WidgetCategoryId { set;get;}
//Other properties
}
Send that to the Add View (HttpGet)
public ActionResult Add(int id)
{
Widget objModel=new Widget{ WidgetCategoryId =id} ;
return View(objModel);
}
Now in your Add View, Keep that in a hiddden variable using the HiddenFor HTML helper method.
#using MyProject.Models
#model Widget
#{
ViewBag.Title = "My Title";
}
#using (Html.BeginForm())
{
#Html.HiddenFor(m=>m.WidgetCategoryId);
<input type="submit" value="Save" />
}
Now you will have it in your HTTPPost action method when you submit.
[HttpPost]
public ActionResult Add(Widget model)
{
// check model.WidgetCategoryId and Have fun with it
}
default model binder checks for request parameters by name and attempts to set properties on model according. If you need something more evolute, you can take a look to custom model binding. Btw, you can change your action to:
public ActionResult Add(Widget widget) // Widget class has one property named Id with public set
{
return View(widget);
}
or
public ActionResult Add(int id) // 'id' is the WidgetCategoryID
{
Widget widget = new Widget();
widget.Id = id;
return View(widget);
}
I slightly prefer the second form for creations, but I guess it's a matter of tastes
Btw, your view's form shall have inputs for each "important" property of the Widget object. (Hidden or text) via:
#Html.HiddenFor (m => m.Id);
Within your view code, you're not only specifying that the view will return a Widget type, but specifying that the entire model for that view is a Widget type. Basically, that data passed both into and out of the View via its #model is of type Widget.
What you can do here is to retain the strong-typing of the View to a Widget, but where you need to pass in simply an ID value (a simple int), you can use either the ViewData or the ViewBag
For example, in the controller:
public ActionResult Add(int id) // 'id' is the WidgetCategoryID
{
// All properties of a ViewBag are completely dynamic!
ViewBag.WidgetID = id;
// You're still returning a View strongly-typed to a Widget, but not
// actually supplying a Widget instance.
return View();
}
And in the View:
#using MyProject.Models
#model Widget
#{
ViewBag.Title = "My Title";
Layout = "~/Views/Shared/_MyLayout.cshtml";
}
#using (Html.BeginForm())
{
// Retrieve the WidgetID from the ViewBag
var WidgetID = ViewBag.WidgetID;
// Do something with the WidgetID, for example:
#Html.Hidden(WidgetID)
}
Note that ViewData and ViewBag are very similar mechanisms by which "non-model" data can be passed into a view. ViewBag is newer (MVC 3) and is based upon the dynamic features of C#4.0, whereas ViewData is the older method based upon a collection of key/value pairs.
In the Add Action in the Controller you can set a ViewBag property to the id and them in the View do html.Hidden() using the ViewBag property. I.e.,
public ActionResult Add(int id) // 'id' is the WidgetCategoryID
{
Widget widget = new Widget();
widget.Id = id;
ViewBag.categoryId = id;
return View(widget);
}
In the View
#Html.Hidden(#:ViewBag.categoryId)
This question has been asked in various forms but none of the answers seem to fit my situation. I am simply trying to retrieve the selected value of a dropdown list in my controller.
Here is my code:
ViewModel.cs
public class ViewModel
{
public ViewModel() {}
public ViewModel(Contact contact, IEnumerable<State> states)
{
this.Contact = contact;
this.States = new SelectList(states, "Id", "Name", contact.StateId);
}
public Contact Contact {get;set;}
public SelectList States {get;set;}
}
Controller.cs
[HttpPost]
public ActionResult Edit(ViewModel viewModel)
{
_contactService.UpdateContact(viewModel.Contact);
return RedirectToAction("Item", new {id = viewModel.Contact.Id});
}
View.cshtml
<button type="submit" onclick="javascript:document.update.submit()"><span>Update</span></button>//aesthic usage.
#{using (Html.BeginForm("Edit", "Controller", FormMethod.Post, new { name = "update" }))
{
#Html.HiddenFor(m => m.Contact.Id)
#Html.LabelFor(m => m.Contact.Name, "Name:")
#Html.TextBoxFor(m => m.Contact.Name)
<label for="state">State:</label>
#Html.DropDownList("state", Model.States)
}
}
Everything works as expected except that no values from the dropdownlist are passed in my posted viewModel to the controller. The edit page and all fields load correctly. The dropdowns bind correctly and have their selected values displayed properly. However, when I post I only get a "Contact" object passed to the controller. The "States" SelectList object is null.
I tried mapping a "StateId" property in my viewModel contstructor but that did not work either. What am I doing wrong?
Thanks.
I hate answering my own questions but based on the multiple issues I had coupled with the myriad of answers available I thought I would summarize my findings.
First off thanks for Filip, his answer did not exactly fix my problem but it led me in the right direction. +1
If you are creating a form for viewing and editing that requires a drop down list, here are some suggestions and gotchas. I will start with a list of parameters that I needed to fit my needs.
Strongly typed views in my view are preferable. Minimize magic strings.
View models should contain as little logic and extraneous elements as possible. There only job should be to facilitate a collection of data objects.
The drop down list should display the selected value.
The selected value should map easily back to the view model on form submit.
This may sound like an obvious and easily obtainable list but for someone new to MVC, it is not. I will revise my code from above with comments. Here is what I did.
ViewModel.cs
public class ViewModel
{
public ViewModel() {}
public ViewModel(Contact contact, IList<State> states)
{
//no need to pass in a SelectList or IEnumerable, just what your service or repository spits out
this.Contact = contact;
this.States = states;
}
public Contact Contact {get;set;}
public IList<State> States {get;set;}
}
Controller.cs //nothing really different than above
public ActionResult Edit(int id)
{
var contact = _contactService.GetContactById(id);
var states = _stateService.GetAllStates();
return View(new ViewModel(contact, states));
}
public ActionResult Edit(ViewModel viewModel)
{
_contactService.UpdateContact(viewModel.Contact);
return RedirectToAction("Edit", new {id = viewModel.Contact.Id });
}
View//thanks goes to Artirto at this post
#{using (Html.BeginForm("Edit", "Controller", FormMethod.Post))
{
#Html.HiddenFor(m => m.Contact.Id)
#Html.DropDownListFor(m => m.Contact.StateId, new SelectList(Model.States, "Id", "Name", #Model.Contact.StateId))
<input type="submit" value="Save" />
}
}
Try using #Html.DropDownListFor instead, if "state" is a part of your model you can use it like this:
#Html.DropDownListFor(m => m.Contact.StateId, Model.States, "-- Please select a State --") where m.State holds the selected value.
Also not to confuse the IEnumerable with the Model, I would put that in the ViewBag / ViewData.
It would look something like this instead:
#Html.DropDownListFor(m => m.Contact.StateId, (IEnumerable<string>)ViewBag.States, "-- Please select a State --")
And in your action that returns this view you will need to initialize the State enumerable to the ViewBag.States property.