I have a model with a foreignkey to another models. From this foreignkey, i want to show into my form a DropDownList with the name of my second models. When i go to create.cshtml or edit.cshtml i have the following error:
ArgumentNullException: Value cannot be null.
Here's my code:
// Models
public class Timesheet
{
public Pharmacy Pharmacy { get; set; }
}
// Controllers / ame code on edit.cshtml.cs
public IActionResult OnGet()
{
Dictionary<int, string> pharmacies = new Dictionary<int, string>();
foreach (Pharmacy p in _context.Pharmacy)
pharmacies.Add(p.PharmacyID, p.Name);
ViewData["PharmacyID"] = pharmacies;
return Page();
}
Into create/edit.cshtml, the HTML are:
#Html.DropDownListFor(model => model.ViewData["PharmacyID"], new SelectList(ViewBag.pharmacies , "key", "value"),"-- select --")
I hope you can help me to fix it :)
Thanks per advance !
The problem is you're binding to ViewData using lambda expression instead of model property with value type and using unassigned ViewBag.pharmacies, hence the binding doesn't work as intended.
You should create a model property first:
[BindProperty]
public int PharmacyID { get; set; }
And then bound it to DropDownListFor helper:
#Html.DropDownListFor(model => model.PharmacyID, new SelectList(ViewData["PharmacyID"], "Key", "Value"),"-- select --")
Or use <select> tag helper:
// Page controller (cshtml.cs)
ViewData["PharmacyID"] = new SelectList(pharmacies, "Key", "Value");
<!-- CSHTML view -->
<select asp-for="PharmacyID" asp-items="#ViewData["PharmacyID"]" ...></select>
Related
When creating Employee entity you are supposed to select MeetingCenterfrom DropDownList. All MeetingCenters show just fine in DropDownList with their Names, but when some of them is selected and Employee is created Meeting Center is null. Im using NoSQL DocumentDB database.
Controller:
[ActionName("Create")]
public async Task<IActionResult> CreateAsync()
{
ViewBag.MeetingCentersList = await _meetingCenterReposiotry.GetItemsAsync();
return View();
}
Create View:
#model Employee
#using (Html.BeginForm("Create", "Home", FormMethod.Post, new { #class = "form-horizontal" }))
{
...
<div class="form-group">
#Html.LabelFor(MeetingCenter => Model.MeetingCenter, new { #class = "control-label" })
#Html.DropDownListFor(MeetingCenter => Model.MeetingCenter, new SelectList(ViewBag.MeetingCentersList, "MeetingCenterId", "Name"), new { #class = "form-control" })
</div>
...
}
Piece of Employee Model
public class Employee
{
...
[JsonProperty(PropertyName = "id")]
public string EmployeeId { get; set; }
[JsonProperty(PropertyName = "meetingCenter")]
public MeetingCenter MeetingCenter { get; set; }
...
}
Piece of MeetingCenter Model
public class MeetingCenter
{
...
[JsonProperty(PropertyName = "id")]
public string MeetingCenterId { get; set; }
...
}
With your current code, the DropDownListFor helper will render a SELECT element with options, which has the MeetingCenterId as the value attribute and the Name as the Text. The SELECT element's name attribute value will be MeetingCenter. So when the form is submitted the form data will look like this
MeetingCenter: 2
Assuming user selected the option with value "2".
But the MeetingCenter property of your view model(Employee) is not a numeric type, it is a complex type(MeetingCenter). So model binder cannot map this value to MeetingCenter property of your view model.
You can render the SELECT element with the name MeetingCenter.MeetingCenterId and then model binder will be able to map the posted form data as the input element name matches with the naming-structure of your view model.
So you should render something like this inside your form.
<select name="MeetingCenter.MeetingCenterId">
</select>
You can generate the above markup by using the SELECT tag helper and specifying MeetingCenter.MeetingCenterId as the asp-for property.
<select asp-for="MeetingCenter.MeetingCenterId"
asp-items="#(new SelectList(ViewBag.MeetingCentersList,
"MeetingCenterId", "Title"))">
</select>
Now when the form is submitted, it will populate the MeetingCenter property of your view model and it's MeetingCenterId property.
If you want the full MeetingCenter property to be populated (properties other than MeetingCenterId, get the full object by querying the data provided by _meetingCenterReposiotry.GetItemsAsync() using the MeetingCenterId available to you in the HttpPost action. Something like this
var id = viewModel.MeetingCenter.MeetingCenterId;
var items = await _meetingCenterReposiotry.GetItemsAsync();
var item = items.FirstOrDefault(a=>a.MeetingCenterId==id);
// User item now
// May be do somethig like : viewModel.MeetingCenter = item;
I also suggest you to use the correct types. If MeetingCenterId is numeric value, use int as type instead of string
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
(First topic for me -> Amazing !)
I'm actually learning how to Handle ASP.NET MVC and i'm having some problem with he Html Helper DropDownListFor.
So the class concerned looks like that :
public class Auteur {
[required]
public int Id {get; set;}
public string Nom {get; set;}
}
My controller looks like that :
[HttpGet]
public ActionResult Ajout_livre()
{
Donnee_a_persister data = new Donnee_a_persister();
List<Auteur> auteur_possible = new List<Auteur>();
foreach (Auteur temp in data.afficher_auteurs())
auteur_possible.Add(temp);
ViewBag.auteur_possible = new SelectList(auteur_possible,"Id","Nom");
return View();
}
And I use it in my view like that :
<div>
#Html.LabelFor(model => model.Auteur_du_livre)
#Html.DropDownListFor(model => model.Auteur_du_livre, (SelectList)ViewBag.auteur_possible)
#Html.ValidationMessageFor(model => model.Auteur_du_livre)
</div>
Here is my post-method (as you can see i tried to populate the select list here too)
[HttpPost]
public ActionResult Ajout_livre(Livre detaillivreaajouter)
{
Donnee_a_persister data = new Donnee_a_persister();
List<Auteur> auteur_possible = new List<Auteur>();
foreach (Auteur temp in data.afficher_auteurs())
auteur_possible.Add(temp);
ViewBag.auteur_possible = new SelectList(auteur_possible, "Id", "Nom");
if (ModelState.IsValid == false)
{
return View(detaillivreaajouter);
}
//Work out
return View("Livre_eligible", detaillivreaajouter);
}
Auteur_du_livre reffers to a variable of type Auteur as this view is strongly typed.
So I get the view and my dropdown list is populated with the content of the ViewBag but when I submit the form I get the following error
There is no ViewData item of type 'IEnumerable' that has the key "Auteur_du_Livre"
I've seen that this problem is frequent so i've tried a lot of thing but i can't figure out what is really happening
Is it possible to get the whole object directly from the form(due to the binding) or do I have to get the property Id and find the corresponding Object in my post method ?
I finally get what's wrong in my Model : i thought that the binding made with the SelectList could directly get the object of type Auteur when the user submit the form but it's wrong. The binding can only get a primitive variable (here it would get the property Id of type int) that was assert on the value fo the form via the selectlist.
Thanks for all.
So i've just changed the helper in my view in the following way :
#Html.DropDownListFor(model => model.Auteur_du_livre.Id, (SelectList)ViewBag.auteur_possible)
I believe this will create a list in my HomeController. But not sure what calls it or where it goes in the Controller beside maybe the first Add ActionResult (GET method).
public static IEnumerable<SelectListItem> items()
{
using (oesacEntities_compact db = new oesacEntities_compact())
{
var query = from s in db.tblSponsors select new { s.SponsorID, s.BizName };
return query.AsEnumerable()
.Select(x => new SelectListItem
{
Value=x.SponsorID.ToString(),
Text = x.BizName
}).ToList();
}
}
I can't seem to send it to the Add view or to reference it from the Add view:
<div class="editor=field">
#Html.DropDownListFor(model => model.SponsorID,IEnumerable<SelectListItem> SelectList);
</div>
It seems so simple in other coding languages. I want to populate a pulldown with about 200 sponsor ID's for value, BizNames for text. For now at least. God help me after that when I want to show an Edit view with the value selected.
thankyou stackoverflow
You need to pass the SelectList to your view. Ideally your view model should include a property for the SelectList but you can (yuk) use ViewBag, for example
View Model
public class MyViewModel
{
public int SponsorID { get; set; }
// other properties
public SelectList SponsorList { get; set; }
}
Controller
public ActionResult SomeThing()
{
MyViewModel model = new MyViewModel();
// assign the select list
var sponsors = from s in db.tblSponsors;
model.SponsorList = new SelecList(sponsors, "SponsorID", "BizName");
return View(model);
}
View
#Html.DropDownListFor(model => model.SponsorID, Model.SponsorList);
or if you assigned the select list to ViewBag
#Html.DropDownListFor(model => model.SponsorID, (SelectList)ViewBag.SponsorList);
my view:
#using (Html.BeginForm("Index", "Person"))
{
#Html.DropDownList("Departments",ViewData["Departments"] as SelectList)
<button type="submit">Select</button>
}
my DepartmentController:
public ActionResult Index()
{
ViewData["Departments"] = new SelectList(db.Departments, "ID", "Name");
return View();
}
my PersonController
public ActionResult Index(string id = null)
{
if (id != null)
{
//Return list of persons with department id
}
return View();
}
My problem:
When I select a department from the DropDown and press the button, it redirects fine, but the id is not passed.
What am I missing? I'm guessing it has to do with how i fill the DropDownList?
Anyway, as always, thanks in advance
The name attribute of your dropdown is not "id" so the MVC model binder can not bind it.
Add html attribute new {#Name ='id'} to your DropDown definition and it should work.
I also would suggest that your view receive a model - in this case model binding will be much easier, and you could use DropDownFor helper.
Using model also allows you to avoid using ViewData and ViewBag containers that are not recommended because they are not strongly typed so if by mistake you write ViewData["Departnents"] in your View you won't get a compilation error because of the typo, but clearly it won't work.
As an opposite you can define a model
public class Person
{
public SelectList Departments {get; set;}
public int SelectedDepatrmentId {get; set;}
//Other person properties come here
}
In your View the only thing you should do to make it work is:
#model path to your Person class
#Html.DropDownListFor(model => model.SelectedDepatrmentId, Model.Departments)
The problem in your case that mvc model binding is done with name attribute and #Html.DropDownList("Departments"... will render html with dropdown having name 'Departments' so either try my first answer or change #Html.DropDownList("Departments"... as shown in my second answer.
Try This :
public ActionResult Index(string Departments) // <------ Use 'Departments' here instead of 'id'
{
.....
return View();
}
OR change dropdownlist as :
#Html.DropDownList("id",ViewData["Departments"] as SelectList)