Mvc3 Adding user roles in tightly bound dropdownbox - asp.net-mvc-3

I have a register model thats tighly bound to a register view. The register model has a selectlistitem poperty. Im having trouble populating a dropdownbox with the select list items.
var users = Roles.GetAllRoles();
model.UserRoles = users.Select(m => new SelectListItem()
{Value = m.ToString(),Text = m.ToString()})
#Html.DropDownListFor(r=>r.ToString(),Model.UserRoles,"Select Role")

You need to bind the selected value to a property of your model, like so:
(in your Model):
public string ActiveRole { get; set; }
...
(in your View):
#Html.DropDownListFor(m => m.ActiveRole, Model.UserRoles,"Select Role")

The first argument of the DropDownListFor method is the property which holds the value of the list. If the value in the list will be the id of the role this has to have the argument x => x.RoleId where x is your model and RoleId is the property in the model. You are binding the dropdownlist to the string representation of the viewmodel, the result of which is garbage.
The second argument is an IEnumerable<SelectListItem> which holds the options for the list. This object has the following values: Value is the value that is send back to the server, this should be what identifies the role, often the id of the role. Text is the text that is displayed in the list, it is what the user sees. Selected is a boolean that indicates which item in the list is selected. If it is false in all of the SelectListItems, the top one is selected, which is standard HTML behaviour.
In your case, you are placing the SelectListItems in the model, which is an unnecessary step, but should work. The model binder won't understand the first argument though, so you need to fix that one.

Related

hiddenFor helper is pulling id from action parameter, not the viewModel

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)

MVC3 DropDownListFor not setting selected property

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.

Where does DropDownListFor get the model from?

You can create a dropdownlist using
#Html.DropDownListFor(model => model.SelectedId, model.DropDownItems);
but where does DropDownListFor get the value to pass into the model => model.SelectedId lambda from?
SelectedId is just the integer of the field in the model.
the model is passed in from the controller using
return View(myModel);
And the model can be defined in the view at the top
#model mymodelnamespace.RoomBookingInsert
So the dropdownbox's value is set as the SelectedId field in your model.
A proper field name for example would be RoomNo if that clarifies it better.
#Html.DropDownListFor(model => model.RoomNo, model.Rooms);
As long as model.DropDownItems (or model.Rooms in my example) Contains a item with the value of that attribute, it will set it as the selected item in the list as default.
Edit:
If you are using viewbag, rather than the model, instead use DropDownList. (each input has a for extension for use with models)
#Html.DropDownList("RoomNo",
new SelectList(ViewBag.Rooms as System.Collections.IEnumerable ?? Enumerable.Empty<SelectListItem>(), "RoomNo", "RoomName"),
new { #value = #ViewBag.RoomNo})
This above, creates an input form with id RoomNo,
and uses a SelectList that will default to empty if null. The fields that defined the values and text shown are set at the end of the select list parameters.
The last bit sets the default value to the contents of #ViewBag.RoomNo.
You can also create the select list in the controller, and then simply set the dropdown options to the viewbag.myselectList entity.

MVC3 Simple drop down list

I have an MVC3 C#.Net web site and I have a lookup table "Methods" in SQL server. I want to create a drop down list that populates the list of values from the "Name" column in my "Methods" table. I have an object, "Task", that has a string property "MethodName". I want to attach the selected value from the Drop Down list to this property in the Task object. How do I do this?
Make a model that contains a task and a list of methods.  Get all the methods and the task you want from db in your "custom" model. Pass the model into your view
Set in your view in the top #model NameProject.Folder.Modelname 
Then add to your view: 
List<SelectListItem> items = new List< SelectListItem>();
Foreach(var m in Model.Methods)  {
   items.Add(new SelectListItem{Value=#m.Id.toString() , Text=#m.MethodName}) 
}
 Then you could use html helpers that can help you bind model
#Html.DropDownListFor(model => Model.Task.Method, items)
In your controller that gets the post request use your model with task and methods as a parameter then just validate and savechanges

Preselection in a DropDownList

I'm just starting with MVC3 and I have the following situation.
My app's initial sign up page among other controls contains a drop down menu. When the user has completed the form then the form details are saved in a session and they move on to the next step. They may also move back to the original step to re-edit, in which case I need to show the drop down menu with the appropriate value preselected.
My code is as follows:
Controller:
public ActionResult Index()
{
var model = new CompanyDetailsModel();
BindDropDownLists(model);
//IF WE HAVE A SESSION THEN PREFILL THE VALUES
if(MySession.Current.IFA!=null)
model = EditIFAProfileService.returnCompanyDetailSession(MySession.Current.IFA);
return View("CreateCompanyDetails", model);
}
I am getting the expected values from the model, so for example the
value model.Salutation is equal to an integer.
So, armed with that value I would expect to be able to set the preselected value of my dropdownlist as follows:
#Html.DropDownListFor(model => model.SalutationValue, Model.SalutationItems,
"Please Select", new { #tabindex = "1" })
If I do set the model value of SalutationValue to an int then I get an error stating that
ViewData item that has the key is of type 'System.Int32' but must be of type 'IEnumerable'.
Any help would be much appreciated.
Thank you.
Don't use any ViewData if you are working with strongly typed views and view models as it would conflict. Simply set the SalutationValue property to some given value. Here's an example:
public ActionResult Index()
{
var model = new CompanyDetailsModel();
model.SalutationItems = ...
if (MySession.Current.IFA != null)
{
model.SalutationValue = MySession.Current.IFA;
}
return View("CreateCompanyDetails", model);
}

Resources