In MVC3, when using DropDownListFor is it necessary for the first parameter to be a string? I have the following setup:
#Html.DropDownListFor(m => m.MyListItemId, Model.MyListItems,
new Dictionary<string, object>
{
{ "style", "width:120px" },
{ "data-type", "myList" }
})
where m.MyId is an int on my viewmodel. I'm having an issue where when I change the selected item in my drop down list, and inspect the rendered html, the "selected" property is not set to the newly selected item. This is a problem as i'm using jquery clone function to copy that row and i need the list with the new selected item to be copied to my new row. Ideas?
Update - Changing the property on the viewmodel to a string makes no difference.
Is this a bug with mvc dropdownlistfor? I've read quite a few posts on similar issues, but can't seem to find a solution that works in this instance. This is how my list is setup in my code:
var myListItems = _myRepository.GetAll();
model.MyListItems = new SelectList(myListItems, "Id", "Name", lineItem.myListItemId);
model.MyListItemId = lineItem.myListItemId;
where lineItem is passed into this method
No, the selected value property does not need to be a string, it can be an int. As long as the value is convertible to a string, it should work (so selected value type could be a Guid, int, bool, etc).
I have sometimes found issues when my route for the page has a route parameter with the same name as the selected value model property. For example, consider this:
route: "/establishments/{establishmentId}/edit"
Model:
public class MyViewModel
{
public int EstablishmentId { get; set; }
public SelectListItem[] Establishments { get; set; }
}
View:
#Html.DropDownListFor(m => m.EstablishmentId, Model.Establishments)
With this code, the selected value of the drop down list would always be whatever establishmentId is in the route. So if the route were /establishments/12/edit, then value 12 would be selected in the dropdown. It doesn't matter that the route parameter and model property capitalization doesn't match.
I figured this out by downloading the MVC source code, making my own copy of the DropDownListFor (named MyDropDownListFor), then stepping through the code to see what happened. If you are still having trouble with MVC3, I suggest you do the same. You need to figure out whether this is an issue with the server code, or your jquery clone stuff.
Related
I am working in mvc3 razor.
i want checkbox input in razor view for all catagories stored in database.
how could i bind it? please help
Suppose your model is having a field to hold all the checkbox text like.
public class MyClass
{
public string SelectedOptions {get;set;}
public List<String> CheckList {get;set;}
}
Write a controller Action method like
public ActionResult ShowCheckBoxList()
{
MyClass Options= new MyClass();
Options.CheckList = new List<String>();
// Here populate the CheckList with what ever value you have to
// either if you are getting it from database or hardcoding
return View(Options);
}
you have to create a view by right clicking the above method and choose Add View. Make sure you keep the name same as the method name also you the strongly typed view by choosing the MyClass Model from the dropDown, scaffolding template is optional use it as per your scenario.
Now in your View you can use this populated check box text as
#foreach(var optionText in Model.CheckList)
{
#Html.CheckBox(#Model.SelectedOptions, false, new {Name = "SelectedOptions", Value = #optionText})
#Html.Label(#optionText , new { style = "display:inline;" })
}
Keep this in a form and make sure you also specify a Action to be called on post of the form.
Make your action name same as the previous action (it is [GET] meaning before post), Now we create same method for [POST]
[HTTPPOST]
public ActionResult ShowCheckBoxList(MyClass data) // here you will get all the values from UI in data variable
{
//data.SelectedOptions will have all the selected values from checkbox
}
I've stumbled upon a really weird situation with ASP.Net MVC, passing an "id" as an action parameter and an "id" hidden form element. I have an action that has an "id" parameter. This id value represents a project. I have a controller action where I'm creating a data entry form for assigning an employee to the passed in project (I create a drop down list where administrator selects an employee to be assigned to passed project). By assigning an employee to a project, I create a ProjectEmployee record (project id, employee id, and an id that represents the combination which is an identity column in the database). The identity column (which is named "id") is also a hidden form element. This is necessary because I need to be able to edit the project/employee assignment at a later date.
Anyways, when creating a new employee assignment to a project, the project id (which is the "id" being passed to the action) is being applied to the "id" hidden form element.
I'm passing 117 into the action. 117 is the projectId. It's being set to the "id" hidden field which should be 0 because 0 represents a new project/employee assignemnt.
View Model
id - unique id that represents the Project/Employee combination
projectId - the "Id" being passed to action
EmployeeId - what the admin is selecting from drop down
rate
startdate
endDate
...
Data Entry Form
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.ProjectId)
...
Id: #Model.Id <br />
ProjectId: #Model.ProjectId<br />
So, #Html.HiddenFor(m => m.Id) renders a hidden form element with a value of 117. #Model.Id renders 0 to the UI. Stepping through the code I can visually see the value of the Id property to be 0. How come HiddenFor is getting it's wires crossed and pulling the value of 117?
This bug has made it's way into production so I have a mess on my hands with data getting messed up because instead of creating a new record in the database table, I'm actually UPDATING existing records because the "Id" property is erroneously getting set from 0 (which represents a new record) to 117 (which is the projectId) and therefore am updating a different record.
How come HiddenFor is getting it's wires crossed and pulling the value of 117?
That's by design. All HTML helpers such as TextBoxFor and HiddenFor first use the value of ModelState when binding and then the value of the model. I presume your controller action looks like this:
public ActionResult Foo(int id)
{
SomeModel model = ...
// At this stage model.Id = 0
return View(model);
}
The thing is that the default model binder adds a value into the ModelState with the key id which is used by the helper and the model property value is ignored. This is not a bug. It is how the HTML helpers are designed. It's a bit confusing in the beginning when you don't know it, but once you get accustomed to this behavior you don't fall in the trap a second time.
One possibility to fix the problem is to remove this value from ModelState:
public ActionResult Foo(int id)
{
ModelState.Remove("id");
SomeModel model = ...
// At this stage model.Id = 0
return View(model);
}
Or rename the Id property inside your model to something else to avoid the conflict.
As an alternative to changing your field names, you can make a new helper method that doesn't use the route values when evaluating what to put in the value attribute.
I created this helper for the extremely common id case:
public static MvcHtmlString HiddenIdFor<TModel, TValue>(this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> expression)
{
var value = ModelMetadata.FromLambdaExpression(expression, html.ViewData).Model.ToString();
var builder = new TagBuilder("input");
builder.MergeAttribute("type", "hidden");
builder.MergeAttribute("name", ExpressionHelper.GetExpressionText(expression));
builder.MergeAttribute("value", value);
return MvcHtmlString.Create(builder.ToString(TagRenderMode.SelfClosing));
}
Then you can use this in the template:
#Html.HiddenIdFor(m => m.Id)
#Html.HiddenIdFor(m => m.ProjectId)
I am still learning MVC3 and Razor, so this is perhaps a simple question.
On a view I have a DropDownList whose sole purpose is to help filter (via AJAX) a second drop down list:
#Html.DropDownList("Segments", "-- select segment --")
There is a Segments property of the ViewModel that is defined as:
public IEnumerable<SelectListItem> Segments { get; set; }
There is JavaScript that handles the change event for this DropDownList and populates another DropDownList with appropriate values. That other DropDownList is defined like this:
#Html.DropDownListFor(m => m.fafhProdRecId, Enumerable.Empty<SelectListItem>(), "-- select product recommendation --")
This all works fine until I submit. When I submit, I get a validation error on the Segments drop down list!
Now -- there should be absolutely NO validation on the segments DropDownList -- there shouldn't be any client side validation on EITHER drop down list, for that matter.
But when I try to submit, I get the validation error message back:
The value '1' is invalid.
I have no idea why this is happening.
I have no idea how to decorate the Segments property to say that it is NOT required.
I have no idea how to tell the unobtrusive javascript validator that it is, in fact, being quite obtrusive.
In your ViewModel class add [Bind(Exclude = "Segments")]
From: Using Data Annotations for Model Validation
make sure that your Model has fafhProdRecId as nullable, I imagine it's declared as:
public int fafhProdRecId { get; set; }
change this to:
public int? fafhProdRecId { get; set; }
hopefully, that should resolve the issue as this effectively makes the model field nullable (assuming the db field IS nullable too of course).
I have a ddl which is populated with hours of day 01-23. This is on a form which is used to book an item of equipment. The hour is populated to a db field. The issue is this, when the booking form is opened to alter the time the ddl shows the hour that was booked, when changed though and the form is submitted the value passed on post is the initial value from db not the new selected hour.
this is the basic pieces of code. any idea why the newly selected ddl value is not passed to the model??
View
<%= Html.DropDownList("ddl_Hour", Model.ddlHour,
new { #class = "DropDown", style = "width: 40px” })%>
Model
private string _ddlHourSelectedValue = "0";
public SelectList ddlHour
{
get
{
return (new System.Web.Mvc.SelectList(_ddlHour, "intValue", "Text", Convert.ToInt32(_ddlHourSelectedValue)));
}
}
public string ddlHourSelectedValue
{
get
{
return _ddlHourSelectedValue;
}
set
{
_ddlHourSelectedValue = value;
}
}
param[6] = new SqlParameter("#Timeslot", ddlHourSelectedValue);
The field in your view is called "ddl_Hour" However is there a variable in your Model with the same name? Otherwise the MVC framework will not automatically populate the value in the model.
Two ways you could go about this.
1
In your controller methods that accepts a post, you can add the parameter: FormCollection fc to the method. This key value pair collection will allow you to fetch results from fields in the post data like so:
string selectedValue = fc["ddl_Hour"];
2
Or you can modify your model to include a variable with the same name as the drop down list so that it is automatically populated for you.
public string ddl_Hour { get; set; }
You should then be able to access the result of the drop down list selection on post from that variable.
I'm running into an issue trying to use #Html.DropDownListFor().
I have a model with a navigation property on it:
public class Thing {
...
public virtual Vendor Vendor { get; set; }
}
In the controller I'm grabbing the vendor list to throw into the ViewBag:
public ActionResult Create() {
ViewBag.Vendors = Vendor.GetVendors(SessionHelper.CurrentUser.Unit_Id);
return View();
}
The html item in the view looks like this:
#Html.DropDownListFor(model => model.Vendor, new SelectList(ViewBag.Vendors, "Id", "Name"), "---- Select vendor ----")
#Html.ValidationMessageFor(model => model.Vendor)
The dropdown list is being rendered, and everything seems fine until I submit the form. The HttpPost Create method is returning false on the ModelState.IsValid and throwing a Model Error: The parameter conversion from type 'System.String' to type '...Models.Vendor' failed because no type converter can convert between these types.
If I let the page post through, I end up with a server error:
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: items
After searching high and low I haven't been able to find a reason that the #Html.DropDownListFor() isn't properly auto-binding a Vendor object to the navigation property.
Any help would be greatly appreciated.
EDIT:
I ended up having to explicitly set the ForeignKey attributes so that I could directly access "Vendor_Id" then I changed the DropDownListFor to point to "Vendor_Id" instead of the navigation property. That seems to work.
I have found that the best way to do this is as follows. Change the controller to create the SelectListItems.
public ActionResult Create() {
ViewBag.Vendors = Vendor.GetVendors(SessionHelper.CurrentUser.Unit_Id)
.Select(option => new SelectListItem
{
Text = (option == null ? "None" : option.Name),
Value = option.Id.ToString()
});
return View();
}
Then modify the view as follows:
#Html.DropDownListFor(model => model.Vendor, (IEnumerable<SelectListItem>)ViewBag.Vendors, "---- Select vendor ----")
#Html.ValidationMessageFor(model => model.Vendor)
You have to cast the ViewBag.Vendors as (IEnumerable).
This keeps the views nice and neat. You could also move the code that gets the SelectListItems to your repo and put it in a method called something like GetVendorsList().
public IEnumerable<SelectListItem> GetVendorsList(int unitId){
return Vendor.GetVendors(unitId)
.Select(option => new SelectListItem
{
Text = (option == null ? "None" : option.Name),
Value = option.Id.ToString()
});
}
This would separate concerns nicely and keep your controller tidy.
Good luck
I have replied similar question in following stackoverflow question. The answer is good for this question too.
Validation for Navigation Properties in MVC (4) and EF (4)
This approach doesn't publish the SelectList in controller. I don't think publishing SelectList in controller is good idea, because this means we are taking care of view part in controller, which is clearly not the separation of concerns.