Как вы обрабатываете несколько кнопок отправки в ASP.NET MVC Framework? - PullRequest
699 голосов
/ 14 января 2009

Есть ли какой-нибудь простой способ обработки нескольких кнопок отправки из одной формы? Пример:

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" value="Send" />
<input type="submit" value="Cancel" />
<% Html.EndForm(); %>

Есть идеи, как это сделать в бета-версии ASP.NET Framework? Все примеры, на которые я гуглил, содержат отдельные кнопки.

Ответы [ 35 ]

593 голосов
/ 18 августа 2011

Вот наиболее чистое решение, основанное на атрибутах, для проблемы с несколькими кнопками отправки, основанное на публикации и комментариях от Maartin Balliauw .

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class MultipleButtonAttribute : ActionNameSelectorAttribute
{
    public string Name { get; set; }
    public string Argument { get; set; }

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {
        var isValidName = false;
        var keyValue = string.Format("{0}:{1}", Name, Argument);
        var value = controllerContext.Controller.ValueProvider.GetValue(keyValue);

        if (value != null)
        {
            controllerContext.Controller.ControllerContext.RouteData.Values[Name] = Argument;
            isValidName = true;
        }

        return isValidName;
    }
}

бритва:

<form action="" method="post">
 <input type="submit" value="Save" name="action:Save" />
 <input type="submit" value="Cancel" name="action:Cancel" />
</form>

и контроллер:

[HttpPost]
[MultipleButton(Name = "action", Argument = "Save")]
public ActionResult Save(MessageModel mm) { ... }

[HttpPost]
[MultipleButton(Name = "action", Argument = "Cancel")]
public ActionResult Cancel(MessageModel mm) { ... }

Обновление: Страницы Razor обеспечивает такую ​​же функциональность из коробки. Для новых разработок это может быть предпочтительнее.

455 голосов
/ 14 января 2009

Дайте вашим кнопкам отправки имя, а затем проверьте отправленное значение в методе вашего контроллера:

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="submitButton" value="Send" />
<input type="submit" name="submitButton" value="Cancel" />
<% Html.EndForm(); %>

отправка

public class MyController : Controller {
    public ActionResult MyAction(string submitButton) {
        switch(submitButton) {
            case "Send":
                // delegate sending to another controller action
                return(Send());
            case "Cancel":
                // call another action to perform the cancellation
                return(Cancel());
            default:
                // If they've submitted the form without a submitButton, 
                // just return the view again.
                return(View());
        }
    }

    private ActionResult Cancel() {
        // process the cancellation request here.
        return(View("Cancelled"));
    }

    private ActionResult Send() {
        // perform the actual send operation here.
        return(View("SendConfirmed"));
    }

}

EDIT:

Чтобы расширить этот подход для работы с локализованными сайтами, изолируйте свои сообщения в другом месте (например, компилируйте файл ресурса в строго типизированный класс ресурса)

Затем измените код так, чтобы он работал следующим образом:

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="submitButton" value="<%= Html.Encode(Resources.Messages.Send)%>" />
<input type="submit" name="submitButton" value="<%=Html.Encode(Resources.Messages.Cancel)%>" />
<% Html.EndForm(); %>

и ваш контроллер должен выглядеть так:

// Note that the localized resources aren't constants, so 
// we can't use a switch statement.

if (submitButton == Resources.Messages.Send) { 
    // delegate sending to another controller action
    return(Send());

} else if (submitButton == Resources.Messages.Cancel) {
     // call another action to perform the cancellation
     return(Cancel());
}
119 голосов
/ 14 января 2009

Вы можете проверить имя в действии, как было упомянуто, но вы можете подумать, является ли это хорошим дизайном. Это хорошая идея, чтобы рассмотреть ответственность за действие и не связывать этот дизайн слишком сильно с аспектами пользовательского интерфейса, такими как имена кнопок. Поэтому рассмотрите возможность использования 2 форм и 2 действий:

<% Html.BeginForm("Send", "MyController", FormMethod.Post); %>
<input type="submit" name="button" value="Send" />
<% Html.EndForm(); %>

<% Html.BeginForm("Cancel", "MyController", FormMethod.Post); %>
<input type="submit" name="button" value="Cancel" />
<% Html.EndForm(); %>

Кроме того, в случае «Отмена» вы обычно просто не обрабатываете форму и переходите на новый URL. В этом случае вам не нужно отправлять форму вообще, а просто нужна ссылка:

<%=Html.ActionLink("Cancel", "List", "MyController") %>
95 голосов
/ 21 января 2009

Эйлон предлагает вам сделать это так:

Если у вас более одной кнопки, вы можно различить, дав каждая кнопка имя:

<input type="submit" name="SaveButton" value="Save data" />
<input type="submit" name="CancelButton" value="Cancel and go back to main page" />

В вашем методе действия контроллера вы можно добавить параметры, названные в честь Имена тегов ввода HTML:

public ActionResult DoSomeStuff(string saveButton, string
cancelButton, ... other parameters ...)
{ ... }

Если какое-либо значение будет опубликовано в одном из эти параметры, это означает, что была нажата кнопка. Веб-браузер будет публиковать только значение для кнопки one , которая была нажата. Все остальные значения будут нулевыми.

if (saveButton != null) { /* do save logic */ }
if (cancelButton != null) { /* do cancel logic */ }

Мне нравится этот метод, так как он не зависит от свойства value кнопок отправки, которое с большей вероятностью изменится, чем назначенные имена, и не требует включения JavaScript

См: http://forums.asp.net/p/1369617/2865166.aspx#2865166

43 голосов
/ 15 марта 2010

Только что написал пост об этом: Несколько кнопок отправки с ASP.NET MVC :

В основном вместо ActionMethodSelectorAttribute я использую ActionNameSelectorAttribute, что позволяет мне притворяться, что имя действия - это то, что я хочу. К счастью, ActionNameSelectorAttribute не просто заставляет меня указывать имя действия, но я могу выбрать, соответствует ли текущее действие запросу.

Итак, у меня есть класс (кстати, я не слишком люблю это имя):

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;
    }
} 

Чтобы использовать, просто определите форму следующим образом:

<% using (Html.BeginForm("Action", "Post")) { %>
  <!— …form fields… -->
  <input type="submit" name="saveDraft" value="Save Draft" />
  <input type="submit" name="publish" value="Publish" />
<% } %> 

и контроллер с двумя методами

public class PostController : Controller {
    [HttpParamAction]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult SaveDraft(…) {
        //…
    }

    [HttpParamAction]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Publish(…) {
        //…
    } 
}

Как видите, атрибут вообще не требует от вас указывать что-либо. Кроме того, названия кнопок переводятся непосредственно в имена методов. Кроме того (я не пробовал это), они должны работать как обычные действия, так что вы можете публиковать сообщения на любой из них непосредственно.

35 голосов
/ 22 апреля 2010

Я бы предложил заинтересованным сторонам взглянуть на решение Мартена Баллиау . Я думаю, что это очень элегантно.

В случае исчезновения ссылки используется атрибут MultiButton, примененный к действию контроллера, чтобы указать, к какому нажатию кнопки должно относиться это действие.

31 голосов
/ 28 мая 2016

это короткий и люкс:

Ответили Jeroen Dop

<input type="submit" name="submitbutton1" value="submit1" />
<input type="submit" name="submitbutton2" value="submit2" />

и делайте так в коде behinde

 if( Request.Form["submitbutton1"] != null)
{
    // Code for function 1
}
else if(Request.Form["submitButton2"] != null )
{
       // code for function 2
}

Удачи.

21 голосов
/ 14 января 2009

Вы должны быть в состоянии назвать кнопки и дать им значение; затем сопоставьте это имя в качестве аргумента действия. В качестве альтернативы, используйте 2 отдельные action-ссылки или 2 формы.

13 голосов
/ 14 января 2009

Вы можете написать:

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="button" value="Send" />
<input type="submit" name="button" value="Cancel" />
<% Html.EndForm(); %>

А затем на странице проверьте, если имя == «Отправить» или имя == «Отмена» ...

12 голосов
/ 15 декабря 2013

Что мне не нравится в ActionSelectName, так это то, что IsValidName вызывается для каждого метода действия в контроллере; Я не знаю, почему это так работает. Мне нравится решение, в котором каждая кнопка имеет свое имя в зависимости от того, что она делает, но мне не нравится тот факт, что у вас должно быть столько параметров в методе действия, сколько кнопок в форме. Я создал перечисление для всех типов кнопок:

public enum ButtonType
{
    Submit,
    Cancel,
    Delete
}

Вместо ActionSelectName я использую ActionFilter:

public class MultipleButtonsEnumAttribute : ActionFilterAttribute
{
    public Type EnumType { get; set; }

    public MultipleButtonsEnumAttribute(Type enumType)
    {
        EnumType = enumType;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        foreach (var key in filterContext.HttpContext.Request.Form.AllKeys)
        {
            if (Enum.IsDefined(EnumType, key))
            {
                var pDesc = filterContext.ActionDescriptor.GetParameters()
                    .FirstOrDefault(x => x.ParameterType == EnumType);
                filterContext.ActionParameters[pDesc.ParameterName] = Enum.Parse(EnumType, key);
                break;
            }
        }
    }
}

Фильтр найдет имя кнопки в данных формы и, если имя кнопки соответствует любому из типов кнопок, определенных в перечислении, он найдет параметр ButtonType среди параметров действия:

[MultipleButtonsEnumAttribute(typeof(ButtonType))]
public ActionResult Manage(ButtonType buttonPressed, ManageViewModel model)
{
    if (button == ButtonType.Cancel)
    {
        return RedirectToAction("Index", "Home");
    }
    //and so on
    return View(model)
}

и затем в представлениях я могу использовать:

<input type="submit" value="Button Cancel" name="@ButtonType.Cancel" />
<input type="submit" value="Button Submit" name="@ButtonType.Submit" />
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...