Search code examples
asp.net-mvcrazorhtml-helper

DropDownListFor() helper


I've been playing with this HTML Helper for awhile now, following all sorts of examples which in theory accomplish the same end result. However, I can't seem to produce the end result.

Maybe someone can see what I'm doing wrong before I go back to iterating out select options with a foreach. It would be nice to lock in how this helper works, less code than a select with a foreach().

Edit controller:

public ActionResult Edit(string id)
    {
        if (id != "")
        {

            UserViewModel user = (from u in db.Users.Where(u => u.Id.Equals(id))
                                  from ur in u.Roles
                                  join r in db.Roles on ur.RoleId equals r.Id

                                  select new UserViewModel
                                  {
                                      UserId = u.Id,
                                      DisplayName = u.DisplayName,
                                      Role = r.Name,
                                      RoleId = r.Id

                                  }
                       ).FirstOrDefault();

            //Select List items for select list
            var roles = db.Roles.Select(r => new SelectListItem { Value = r.Id, Text = r.Name });

            //select list with selected value
            user.Roles = new SelectList(roles,"Value", "Text", user.RoleId);

            
            return View(user);
        }
        return View();
    }

View;

<div class="form-group">
   @Html.LabelFor(model => model.Role, htmlAttributes: new { @class = "control-label col-md-2" })

   <div class="col-md-10">
      @Html.DropDownListFor(model => model.Role, Model.Roles, null, new { @class = "form-control" })

      @Html.ValidationMessageFor(model => model.Role, "", new { @class = "text-danger" })
   </div>
</div>

Solution

  • Your code

    var roles = db.Roles.Select(r => new SelectListItem { Value = r.Id, Text = r.Name });
    

    creates IEnumerable<SelectListItem>.

    user.Roles = new SelectList(roles,"Value", "Text", user.RoleId);
    

    just creates another identical IEnumerable<SelectListItem> from it so its just pointless extra overhead and since your binding to a property in your model, the last paramater (user.RoleId) is ignored and also pointless

    It should be just

    user.Roles = db.Roles.Select(r => new SelectListItem { Value = r.Id, Text = r.Name });
    

    When your create this SelectList you setting the value to the Id property of Role but you then attempting to bind to the Role property of UserViewModel instead of the RoleId property. Because the value of Role will not match one of the option values the first option in the dropdownlist will always be selected (because something has to be).

    Change the code in your view to

    @Html.DropDownListFor(model => model.RoleId, Model.Roles, null, new { @class = "form-control" })
    

    If the value of RoleId matches one of the option values, then it will be selected.

    Side note: your Role property in your view model seems unnecessary, as does the need to create a join to the Roles table in your query.