Search code examples
asp.net-mvcrazordropdown

In a Razor View's @html.ActionLink, how do we pass the value of a dropdown list?


So what I am trying to do is that I have a database table of Roles, that I want to display in a dropdown list, and send the value to a different controller function. However, when I try to do this, I do not receive the value of the new Role that is selected from my dropdown list, but instead the value that was previously in my Model. Here is my code for CSHTML:

@model IEnumerable<OnlineStoreData.Model.User>
<h4>List of Users: </h4>

<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.UserName)
        </th>
        <th></th>
    </tr>

@foreach (var user in Model) {
    if (user.Role.RoleName.TrimEnd(' ') == "User")
    {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => user.UserName)
        </td>
        <td>
            @Html.DropDownListFor(modelItem => user.Role.RoleName, new SelectList(ViewBag.RoleList)) //Here I need to select a new Role, for example "Admin"
            @Html.ActionLink("Promote", "Promote", new { id = user.UserId, role = user.Role.RoleName }) |  
            @Html.ActionLink("Delete", "Delete", new { id = user.UserId })
        </td>
    </tr>
    }
}

</table>

and this is the code for my Controller

public ActionResult ManageUsers()
{
    ViewBag.RoleList = storeDBEntities.Roles.Select(role => role.RoleName).ToList();
    return View(storeDBEntities.Users.ToList());
}

public ActionResult Promote(int id, string role)
{
    //Here I should get  the new role selected in the dropdown list, but I keep getting "User", which is the old role. 
    User toPromUser = storeDBEntities.Users.Find(id);
    Role newRole = storeDBEntities.Roles.FirstOrDefault(r => r.RoleName == role);
    if(toPromUser != null && newRole != null)
    {
        toPromUser.Role = newRole;
        toPromUser.UserRole = newRole.RoleId;
        storeDBEntities.SaveChanges();
    }
    return RedirectToAction("ManageUsers", "Users");
}

I am not sure how I should fix this to get the code to perform what is intended. Thank you.


Solution

  • The problem is, that you cannot dynamically append the selected value of your dropdown to your action link without JavaScript.

    I think a more elegant approach would be to put your drop down and action buttons in a <form>. That way the method can also be a post, which is a bit more nice in a way, as get operations should not manipulate data.

    <td>
        <form method="post" action="@Url.Action("Promote", new { id = user.UserId })">
            @Html.DropDownList("role", new SelectList(ViewBag.RoleList))
            <button type="submit">Promote</button>
            |  
            @Html.ActionLink("Delete", "Delete", new { id = user.UserId })
        </form>
    </td>
    

    Note that the name of your drop down list should match the name of the role parameter of your controller.


    When that works, you could then add the [HttpPost] attribute to your Promote action, to clarify that this method changes something.

    For your delete action you could do something similar. Either make a second <form> with a different URL, or make it a submit button, too, in the same form, and give the buttons each a name and value. The value of the button you clicked on will be sent to the server - note that I changed the form action URL:

    <td>
        <form method="post" action="@Url.Action("Update", new { id = user.UserId })">
            @Html.DropDownList("role", new SelectList(ViewBag.RoleList))
            <button type="submit" name="operation" value="promote">Promote</button>
            | 
            <button type="submit" name="operation" value="delete">Delete</button>
        </form>
    </td>
    

    And then decide what to do in the controller:

    [HttpPost]
    public ActionResult Update(int id, string operation, string role)
    {
        ...
    

    Lastly, you probably want a confirmation message on the delete action, which can be done so:

    <button type="submit" name="operation" value="delete" onclick="return confirm('Do you really want to delete this user?');">Delete</button>