I am trying to work with an HTML.DropDownList in MVC and am not getting the expected return values. Here is my implementation for the selectList to bind to the drop down -
IEnumerable<status> stat = _provider.GetAllStatuses();
Statuses = new SelectList(stat.ToList(), "id", "name", i.status.id);
And here is my view -
<%= Html.DropDownList("Status",Model.Statuses) %>
I am getting an error when trying to run updatemodel in my controller. I then tried to individually set each object. It turns out that I am not getting a single int from the formvalue as I would expect to. Instead, I am getting a value like "5,10,2,3". I think this is coming from how I set up my selectlist, but I'm not exactly sure. Can anyone see an error in the way I am setting up this dd?
Thanks for any help, and let me know if I can clarify anything.
What does the signature of the post method look like? It (or the model) should have a Status property that's defined as an int. I suspect that you've got more code than you're showing us that is listing all the potential statuses on the page (hidden fields?) and that's what you are seeing posted back as an array of ints.
It should look something like:
public ActionResult PostAction( int status, .... )
{
... status will contain the selected value from the dropdown ...
}
This is how I am doing it:
var stat = _provider.GetAllStatuses();
myViewDataObject.Statuses = new SelectList(stat, "id", "name", i.status.id);
stat is an IEnumerable. Statuses is of type SelectList. You don't need ToList() if you are returning an IEnumerable or IQueryable from your provider.
My view inherits from
System.Web.Mvc.Viewpage<MyProject.Models.MyViewDataClass>
which looks like this:
class MyViewDataClass
{
public int StatusID { get; set; }
public SelectList Statuses { get; set; }
}
In the controller, I am accepting a FormsCollection object, and using the model binder to update it:
public ActionResult Edit(FormCollection collection)
{
var myViewDataObject = new MyViewDataClass();
UpdateModel(myViewDataObject);
}
More info at http://nerddinnerbook.s3.amazonaws.com/Part6.htm
Related
Maybe the title is not so explicitly. Let me explain you my situation
I've got a get and post method in my controller. In the GET method, gets the entities from the database context
[HttpGet]
public ActionResult RecheckAssignment(short id)
{
var assignment = db.Assignments.Find(id);
Session["QuestionList"] = QuestionRepositoryManager.GetAllPossibleQuestionsFromJson(assignment.Content); // it's a list!
return View(Session["QuestionList"]);
}
Assignment entity contains as 10 properties. When I show this entities in the model, it shows uses all the properties, but when the user does post should get only two properties from it (Id string, Changed bool) in the POST METHOD.
I do not what to put inside of the method parameters.
[HttpPost]
public ActionResult RecheckAssignment(...)
{
return View();
}
I put everything in a session variable because later I must have to get the entities again, I guess this is a good option using Session but I'm not sure.
So, what should I have to write inside of the method to get only the Id and Changed properties to updated the entities.
When ASP.NET MVC maps a <form> back to the Action during a POST it will fill in what it can. Consider a class like this:
public class Car
{
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
}
and now consider this form:
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post))
{
Html.TextBoxFor(m => m.Make)
}
and now consider this Action:
public ActionResult ActionName(Car model)
{
// the values of Car will look like this
model.Make // this will be what was in the text box
model.Model // this will be null
model.Year // this will be 0
}
and take note that null and 0 are the default values for those types. So, if I wanted to POST the property Model I need to get it in the form. I can do that with #Html.TextBoxFor, but what if I don't want the user to see it? Well, I can do that too:
Html.HiddenFor(m => m.Model);
and so now when the form is POSTed it will populate the Model with the value it was downloaded with. So, just make sure that all the properties you need are in the form in some way.
I am trying to list the contries in view. I have created a model called tbl_Countries and the code is below
public class tbl_Countries
{
public int ID { get; set; }
public string Country_Name { get; set; }
}
I have a Controller called Home with the following code. I have created an edmx file for TestDB database
public ActionResult Index()
{
TestDBEntities TestdbContext = new TestDBEntities();
var countries = TestdbContext.tbl_Countries.ToList();
return View(countries);
}
Below is my View code
#model IList
displaying the countries using ul li with foreach
If i run the application am getting this error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[TestMVC.tbl_Countries]',
but this dictionary requires a model item of type 'System.Collections.Generic.IList1[TestMVC.Models.tbl_Countries]
I just want to show the list of countries in view and I would like to know
Without creating a model class is it possible to bind grid?
Is it mandatory to specify the model name using #model directive in view?
You got this error 'cause you specified in your view #model List, but pass to it List, try to change it in your view to List
Yes, you can delete #model at all, but in this case your view won't be strongly typed, so you won't be able to use intelli sense
create a list of country type in model
public List< tbl_Countries> country{get;set;}
In index page set the value of this List
public ActionResult Index()
{
TestDBEntities TestdbContext = new TestDBEntities();
tbl_Countries objModel=new tbl_Countries();
objModel.country = TestdbContext.tbl_Countries.ToList();
return View(objModel);
}
According to the error message you are expecting a model of the type List<TestMVC.Models.tbl_Countries> in the view which is different from the List<TestMVC.tbl_Countries> type your action method returns.
To resolve this issue, you could create a list your view expects and map the data you got from Entity Framework to it.
For example:
public ActionResult Index()
{
TestDBEntities TestdbContext = new TestDBEntities();
var countries = new List<TestMVC.Models.tbl_Countries>();
countries = (from country in TestdbContext.tbl_Countries
select new TestMVC.Models.tbl_Countries
{
Country_Name = country.Country_Name
}).toList();
return View(countries);
}
To seperate the logic of the view and data access it is a good practice to have models which are independent from your data models, from the EF models in your example.
Like this poster, I'm a bit confused by ASP.NET MVC Html.ListBoxFor(...). Specifically I'm putting the selection results in a List but after I post the results I'm getting
InvalidOperationException: The ViewData item that has the key 'SelectedDeclarations' is of type 'System.String[]' but must be of type 'IEnumerable<SelectListItem>'
Here is my abbreviated ViewModel that I'm passing to the strongly-typed razor view
public MyViewModel
{
public MyViewModel()
{
(...)
this.VendorsRequiringDeclaration = new List<SelectListItem>();
this.SelectedDeclarations = new List<String>();
}
public IEnumerable<String> SelectedDeclarations { get; set; }
public List<SelectListItem> VendorsRequiringDeclaration { get; set; }
}
and here is the view code that references them
#Html.ListBoxFor(m=>m.SelectedDeclarations, Model.VendorsRequiringDeclaration, new { #class="editor-field", #size=6})
If I change MyViewModel such that SelectedDeclarations is a List of SelectedListItem rather than a List of String, upon post to the appropriate controller action it thinks my model is invalid:
{"The parameter conversion from type 'System.String' to type 'System.Web.Mvc.SelectListItem' failed because no type converter can convert between these types."}
Ideas? I probably have the wrong LINQ expression for the first parameter, but I can't see it from the similar questions. Thanks in advance!
In the case of ModelState is not valid, you need to reset the ViewData object inside the controller.
Since data inside VendorsRequiringDeclaration is not saved anywhere.
As it turns out the problem was SQL permissions on an underlying data table that I am accessing via EF; the post failed and made it appear to be the Html Helper -- apologies for any confusion!
Based on AlexanderB's suggestion though I did rework the Html.ListBoxFor(...) thusly, and it seems to work fine:
#Html.ListBoxFor(m=>m.SelectedDeclarations,
new MultiSelectList(
Model.VendorsRequiringDeclaration,
"Id",
"VendorName",
Model.VendorsRequiringDeclaration.Select(
x => new SelectListItem()
{
Selected = false,
Text = x.VendorName,
Value = x.Id.ToString()
}).ToList()),
new { #class = "editor-field", #size = 6 } )
I'm new to ASP.NET MVC so this could have an obvious answer. Right now I have a form in my view with a lot of input controls, so I have an action that looks like this:
public ActionResult MyAction(string formItemOne, int? formItemTwo, etc...)
It has like a dozen parameters, which is pretty ugly. I'm trying to change it to this:
public ActionResult MyAction(FormCollection formItems)
and then parse the items dynamically. But when I change to a FormCollection, the form items no longer "automagically" remember their values through postbacks. Why would changing to a FormCollection change this behavior? Anything simple I can do to get it working automagically again?
Thanks for the help,
~ Justin
Another solution is to use models instead of manipulating the raw values. Like this:
class MyModel
{
public string ItemOne { get; set; }
public int? ItemTwo { get; set; }
}
Then use this code:
public ActionResult MyAction(MyModel model)
{
// Do things with model.
return this.View(model);
}
In your view:
<%# Page Inherits="System.Web.Mvc.ViewPage<MyModel>" %>
<%= Html.TextBox("ItemOne", Model.ItemOne) %>
<%= Html.TextBox("ItemTwo", Model.ItemTwo) %>
To replace your big list of parameters with a single one, use a view model. If after the POST you return this model to your view, then your view will remember the values posted.
A view model is simply an class with your action parameters as public properties. For example, you could do something like this, replacing:
public ActionResult MyAction(string formItemOne, int? formItemTwo, etc...)
with
public ActionResult MyAction(FormItems formItems)
{
//your code...
return View(formItems);
}
where FormItems is
public class FormItems
{
public property string formItemOne {get; set;}
public property int? formItemTwo {get; set;}
}
You may see a complete example in Stephen Walter's post ASP.NET MVC Tip #50 – Create View Models.
Maybe because they aren't magically inserted into the ModelState dictionary anymore. Try inserting them there.
If you use UpdateModel() or TryUpdateModel() I think the values are gonna be persisted.
I am trying to understand something a bit better with being new to C#, .NET 3.5 and MVC.
I am running through the MVC NerdDinner example and if you look at the ViewModel here: http://nerddinnerbook.s3.amazonaws.com/Part6.htm#highlighter_662935
You can see the Country list and how it gets populated, this seems to work fine but I tried to do a similar thing below using LINQ and I am having problems, with the SelectList approach even though it inherits from the IEnumerable interface.
I have got a task table with a foreign key to a status table. The below code gives me a NullReferenceException when I do a GET on a create action. I can see that an anonymous task object would not have a status set.. so I probably need to check for it, but I dont understand how this is not done for the NerdDinner example??
public class TaskViewModel {
// Properties
public Task Task { get; private set; }
public SelectList Status { get; private set; }
// Constructor
public TaskViewModel(Task task) {
TaskRepository taskRepo = new TaskRepository();
Task = task;
Status = new SelectList(taskRepo.GetStatus(), Task.Status.description);
}
}
//
// GET: /Tasks/Create
public ActionResult Create()
{
Task task = new Task();
return View(new TaskViewModel(task));
}
//Code from TaskRepository
private TaskManagerDataContext db = new TaskManagerDataContext();
public IQueryable<Status> GetStatus() {
return from status in db.Status
orderby status.description
select status;
}
I did another approach using LINQ for the type dropdown and the population of the drop down works but I am yet to test if it selects the correct value once a post is made and the details view is returned. I am also wondering whether this should some how be moved into my repository rather than have a class in my controller doing this sort of thing??
Here is the code:
//In TaskViewModel Class
public IEnumerable<SelectListItem> Types { get; private set; }
//In TaskViewModel constructor
IList<NPType> types = taskRepo.GetTypes().ToList();
Types =
from type in types
select new SelectListItem {
Selected = (type.typeId == task.typeId),
Text = type.description,
Value = type.typeId.ToString()
};
//The TaskForm partial View that is used for the Create action of the TaskController
<p>
<label for="type">type:</label>
<%= Html.DropDownList("Type", Model.Types)%>
<%= Html.ValidationMessage("type", "*") %>
</p>
<p>
<label for="status">status:</label>
<%= Html.DropDownList("Status", Model.Status)%>
<%= Html.ValidationMessage("status", "*") %>
</p>
and the TaskForm view inherits System.Web.Mvc.ViewUserControl
What's in your task constructor? What is the value of .typeId on a newly created task? Is it a null reference?
For the view model sent to your Create view, you shouldn't be trying to set the selected list item unless your task constructor (or other initialization code) sets default values for those properties. If task.typeId is null, then your code that is building the select list will get an error.
I understand that I will get a null value for the type or status if I dont add a value to the newly created task. What I dont understand and which I didnt make clear is the below.
You can see the view model has a Countries property, and its selected value is set to Dinner.Country.. now Dinner.Country is not being set in the create action.. so how come this does not give a null exception?
//viewmodel code
public DinnerFormViewModel(Dinner dinner) {
Dinner = dinner;
Countries = new SelectList(PhoneValidator.Countries, Dinner.Country);
}
//controller code
public ActionResult Create() {
Dinner dinner = new Dinner() {
EventDate = DateTime.Now.AddDays(7)
};
return View(new DinnerFormViewModel(dinner));
}
//view code
<p>
<label for="Country">Country:</label>
<%= Html.DropDownList("Country", Model.Countries) %>
<%= Html.ValidationMessage("Country", "*") %>
</p>
My attempt at trying to understand this better.
//controller code creating a select list in the viewmodel class.
//taskRepo.GetStatus() returns an IQueryable<Status>
Status = new SelectList(taskRepo.GetStatus(), Task.Status);
//MVC Framework SelectList class constructor and ToEnumerable method
public SelectList(IEnumerable items, string dataValueField, string dataTextField, object selectedValue)
: base(items, dataValueField, dataTextField, ToEnumerable(selectedValue)) {
SelectedValue = selectedValue;
}
private static IEnumerable ToEnumerable(object selectedValue) {
return (selectedValue != null) ? new object[] { selectedValue } : null;
}
I can see that SelectList uses its base class of MultiSelectList and that constructor is here:
public MultiSelectList(IEnumerable items, string dataValueField, string dataTextField, IEnumerable selectedValues) {
if (items == null) {
throw new ArgumentNullException("items");
}
Items = items;
DataValueField = dataValueField;
DataTextField = dataTextField;
SelectedValues = selectedValues;
}
When I run this project the html is given as:
<select id="Status" name="Status"><option>NPTaskManager.Models.Status</option>
<option>NPTaskManager.Models.Status</option>
<option>NPTaskManager.Models.Status</option>
<option>NPTaskManager.Models.Status</option>
</select>
Which is to be expected.
If I change the controller code to:
Status = new SelectList(taskRepo.GetStatus(), Task.Status.statusId.ToString(), Task.Status.description);
Then I get a NullReferenceException. Since this is not an ArgumentNullException It seems to me that the root of the exception is not the first SelectList argument. What I am trying to understand is how this all occurs?
Is it because Task.Status needs to be added to Task in the create action of the controller?
I will change this code to use the LINQ approach that I used for the task above, all I am trying to achieve now is some understanding of what is going on.