0

EDIT: Read the bottom of my question to see what I actually did. I accepted a different answer, though because my solution requires AngularJS.


I'm using C# 4.0 and MVC 3/Razor. I'd prefer to leverage existing functionality of the framework rather than make my own helper classes. Here's what I'm doing, simplified:

Here's my model:

MyModel.cs

public class MyModel
{
    public string Name { get; set; }
    public int ID { get; set; }
}

And my view, so far:

Index.cshtml

@model IEnumerable<MyModel>

Please select a Model.

<select name="id">
    @foreach (var m in Model)
    {
        <option value="@(m.ID)">@(m.ID) - @(m.Name)</option>
    }
</select>

And my controller:

MyController.cs

public class MyController : Controller
{
    public ActionResult Index() { /* ... */ }
    public ActionResult Display(int id) { /* ... */ }
    public ActionResult Delete(int id) { /* ... */ }
    public ActionResult Edit(int id) { /* ... */ }
}

In my view, I want to have three submit buttons, each linking to a different controller/action, one for Edit, Delete and Display. However I would like the ID that the user has selected to be passed (via GET). How do I do this?

I know I could make a new controller action whose sole purpose is to determine which of the three to choose, based on the value of a submit type input element, but that approach seems ugly to me; I'd like to bypass that step. Ideally I'd want something like:

@using (Html.BeginForm("", "", FormMethod.Get))
{
    <select name="id">
        @foreach (var m in Model)
        {
            <option value="@(m.ID)">@(m.ID) - @(m.Name)</option>
        }
    </select>

    @Html.SubmitButton("Edit", "My")
    @Html.SubmitButton("Delete", "My")
    @Html.SubmitButton("Display", "My")
}

But I can't find any method signatures in Html that seem to do something like that.


EDIT:

Here's what I ended up going with. It uses a JavaScript library called AngularJS, but requires no additional JavaScript plumbing. It just works.

@model IEnumerable<MyModel>
<html>
    <head>
        <script type="text/javascript" src="../../Scripts/angular.js"></script>
    </head>
    <body ng-app>
        <select ng-model="ID">
            @foreach (var m in Model)
            {
                <option value="@(m.ID)">@(m.ID) - @(m.Name)</option>
            }
        </select>

        @{
            var forms = new[] 
            {
                new { ActionName = "Display", ControllerName = "My" },
                new { ActionName = "Edit", ControllerName = "My" },
                new { ActionName = "Delete", ControllerName = "My" }
            };

            foreach (var form in forms)
            {
                using (Html.BeginForm(form.ActionName, form.ControllerName, FormMethod.Get))
                {
                    <input type="submit" value="@(form.ActionName)" ng-disabled="ID == null"/>
                    <input type="hidden" name="id" value="{{ID}}"/>
                }
            }
        }

    </body>
</html>

2 Answers 2

2

You can (I have) do this using an attribute. Having tried a couple of different variations on the theme I settled with this one:

http://blog.ashmind.com/2010/03/15/multiple-submit-buttons-with-asp-net-mvc-final-solution/

It basically routes based on the name of your submit. For your case:

 <input type="submit" name="delete" value="Delete" />
 <input type="submit" name="Edit" value="Edit" />

and controller

public class MyController : Controller {
    [HttpParamAction]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Edit(int id) {
    }

    [HttpParamAction]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Delete(int id) {

    }
}

the attribute

  public class HttpParamActionAttribute : ActionNameSelectorAttribute {
        public override bool IsValidName(ControllerContext controllerContext,  
                                       string actionName, MethodInfo methodInfo) 


 {

            if (actionName.Equals(methodInfo.Name,  
                                  StringComparison.InvariantCultureIgnoreCase))
                return true;

            if (!actionName.Equals("Action", 
                                   StringComparison.InvariantCultureIgnoreCase))
                return false;

            var request = controllerContext.RequestContext.HttpContext.Request;
            return request[methodInfo.Name] != null;
        }
    }
Sign up to request clarification or add additional context in comments.

Comments

1

For Edit and Display, clearly, you do not need a form submit. You can use GET for that. So Html.ActionLink helper method can be used to show a link which takes the user to approriate screens ( action methods).

To pass the Value of the selected item from the drop down in the link, you can get the selected value with java script on the click event and send that to the action method.

@Html.ActionLink("Edit","Edit","YourController",null ,new {@class="lnk"})
@Html.ActionLink("Display","Display","YourController",null ,new {@class="lnk"})
@Html.ActionLink("Delete","Delete","YourController",null ,new {@class="lnk"})

Now the javascript to capture the click event of these links

$(function(){
  $("a.lnk").click(function(e){
     e.preventDefault();
     var targetUrl=$(this).attr("href");
     var selectedVal=$("#YourDropdownID").val();
     targetUrl=targetUrl+"?id="+selectedVal;
     window.location.href=url;
  });
});

For Delete, you may show a link which takes the user to a confirmation screen and there you can have a submit button which does a HTTPPOST submit.

You can customize your CSS to make the a tag looks like a submit button

Also, Do not render the dropdown using the foreach loop in the view. There is already HTML Helper methods to do that in MVC. You should always make use of that. Here is an answer explaining how to do that.

5 Comments

That approach doesn't let me use an HTML select list, though, does it?
do you want to pass the ID of selected item in dropdown ?
Yes. And I don't want to use JavaScript, because this seems like it should be so simple.
you need to use javascript to handle that.
It seems you're right about JavaScript. However, by using Angular (which I didn't think about until after I asked), I was able to do it very simply and straight-forwardly. No plumbing; Angular just took care of it!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.