Как вы обрабатываете несколько кнопок отправки в ASP.NET MVC Framework?

581

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

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

Любая идея, как это сделать в бета-версии ASP.NET Framework? Все примеры, которые я искал в Google, имеют в них одиночные кнопки.

Теги:
asp.net-mvc
http-post
form-submit

34 ответа

505
Лучший ответ

Вот, в основном, чистое решение на основе атрибутов для проблемы с несколькими кнопками отправки, основанной в основном на сообщении и комментариях от 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) { ... }

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

  • 3
    Я нашел, что это решение - счастливый брак других используемых методов. Работает отлично и не влияет на локализацию.
  • 17
    Отличное решение !! Гораздо больше полезного, чем ответы с самыми высокими оценками
Показать ещё 38 комментариев
448

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

<% 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());
}
  • 0
    Это то, что я искал здесь: stackoverflow.com/questions/649513/… - спасибо
  • 0
    Как раз то, что мне было нужно ... спасибо
Показать ещё 19 комментариев
114

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

<% 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") %>
  • 53
    Это нормально, когда вам не нужны одинаковые данные формы для каждой кнопки отправки. Если вам нужны все данные в общей форме, то Дилан Битти - это то, что вам нужно. Есть ли более элегантный способ сделать это?
  • 3
    Что касается визуального представления, как в этом случае кнопка «Отправить» рядом с кнопкой «Отмена»?
Показать ещё 6 комментариев
90

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

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

<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 ...)
{ ... }

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

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

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

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

  • 2
    Если кто-то сталкивается с этим старым вопросом, это самый чистый ответ, если вы не хотите использовать элементы HTML5 <button>. Если вы не возражаете против HTML5, используйте <button> с атрибутом value.
  • 0
    Как бы вы это сделали, если создали ajax-вызов в этой форме. Кажется, form.serialize () не отвечает на кнопку «Отправить».
Показать ещё 1 комментарий
38

Только что написал сообщение об этом: Несколько кнопок отправки с 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(…) {
        //…
    } 
}

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

  • 1
    Красивая! Я думаю, что это самое элегантное решение. Это исключает значение тега submit из рассмотрения, что является идеальным, поскольку это атрибут pure-UI, который не должен иметь никакого отношения к потоку управления. Вместо этого атрибут уникального name каждого тега submit преобразуется непосредственно в метод дискретного действия на вашем контроллере.
  • 0
    +1 Для меня это, безусловно, лучшее решение этой проблемы. С тех пор, как я его реализовал, я заметил, что через HttpParamActionAttribut проходит много трафика, но по сравнению со всеми другими вещами, которые Asp.Net MVC должен делать при обработке запроса, это вполне приемлемо. Чтобы взломать только то, что мне нужно сделать, это поместить пустое «Действие», названное в моем контроллере, чтобы Resharper не предупредил меня, что действие «Действие» не существует. Большое спасибо!
Показать ещё 1 комментарий
34

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

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

  • 0
    Это решение, которое мы используем сейчас, и оно очень аккуратное. Это только MVC 2?
  • 0
    Это прекрасно! Я не видел этого раньше! Хотя я согласен с тем, что вы, возможно, захотите изменить дизайн любого решения, использующего несколько отправок, чтобы использовать только одну кнопку, я нахожусь в таком положении, что у меня возникают проблемы, и я должен это сделать. Этот ответ должен был победить!
Показать ещё 4 комментария
18

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

На него ответил Jeroen Dop

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

и делать это в коде

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

Удачи.

  • 0
    Потрясающие. именно то, что я делал в веб-формах. Ура приятель
  • 0
    Красиво, просто и выполняет свою работу.
Показать ещё 1 комментарий
17

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

  • 0
    Безусловно, самое чистое и простое решение, которое я видел.
12

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

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

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

  • 1
    Хотя это работает, но я думаю, что неправильно иметь два элемента с одинаковым именем.
  • 1
    Это не обязательно неправильно. Это зависит от того, как вы используете входы. Вы можете иметь несколько элементов с одинаковым именем и ожидать получения нескольких данных (именно так работают радиокнопки и флажки). Но да, если вы используете этот метод, потому что вы делаете это "неправильно" ... Вот почему я поставил "Вы могли бы", а не "Вы должны": P
11

Что-то, что мне не нравится в 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" />
  • 0
    Мне понравилась ваша идея, спасибо!
10

Вот что мне лучше всего подходит:

<input type="submit" value="Delete" name="onDelete" />
<input type="submit" value="Save" name="onSave" />


public ActionResult Practice(MyModel model, string onSave, string onDelete)
{
    if (onDelete != null)
    {
        // Delete the object
        ...
        return EmptyResult();
    }

    // Save the object
    ...
    return EmptyResult();
}
  • 0
    Я получаю нулевое значение для onDelete и onSave в методе контроллера. Ты знаешь почему?
  • 0
    Любой из них не будет нулевым, если вы нажмете соответствующую кнопку. На какую кнопку вы нажимаете, чтобы получить ноль?
7

Я столкнулся с этой "проблемой", но нашел довольно логичное решение, добавив атрибут name. Я не мог вспомнить эту проблему на других языках.

http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2

  • ...
  • Если форма содержит более одной кнопки отправки, только активированная кнопка отправки успешно.
  • ...

Значение следующего кода value может быть изменено, локализовано, интернационализировано без необходимости дополнительного кода, проверяющего файлы или константы с типизированными типизированными ресурсами.

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="send" value="Send" />
<input type="submit" name="cancel" value="Cancel" />
<input type="submit" name="draft" value="Save as draft" />
<% Html.EndForm(); %>`

На принимающей стороне вам нужно будет проверить, не является ли какой-либо из ваших известных типов отправки null

public ActionResult YourAction(YourModel model) {

    if(Request["send"] != null) {

        // we got a send

    }else if(Request["cancel"]) {

        // we got a cancel, but would you really want to post data for this?

    }else if(Request["draft"]) {

        // we got a draft

    }

}
  • 0
    Это решение, которое мы решили использовать для простого веб-приложения, где нам нужна функциональность ASP.NET WebForms, но в рамках MVC.
6

Существует три способа решения указанной проблемы:

  • HTML-способ
  • Способ JQuery
  • "ActionNameSelectorAttribute" способ

Ниже приведено видео, в котором суммируются все три подхода демонстративным образом.

https://www.facebook.com/shivprasad.koirala/videos/vb.100002224977742/809335512483940

HTML-способ: -

В HTML-формате нам нужно создать две формы и поместить кнопку "Отправить" внутри каждой из форм. И каждое действие формы указывает на разные/соответствующие действия. Вы можете увидеть приведенный ниже код, в котором первая форма отправляется на "Action1", а вторая форма будет размещаться в "Action2", в зависимости от того, какая кнопка "Отправить" нажата.

<form action="Action1" method=post>
<input type="submit" name="Submit1"/>
</form>

<form action="Action2" method=post>
<input type="submit" name="Submit2">
</form>

Способ Ajax: -

Если вы любитель Ajax, этот второй вариант вас больше волнует. В способе Ajax мы можем создать две разные функции: "Fun1" и "Fun1", см. Ниже код. Эти функции вызовут Ajax-вызовы с помощью JQUERY или любой другой структуры. Каждая из этих функций привязывается к кнопкам "Отправить" "OnClick". Каждая из этих функций выполняет вызов соответствующих имен действий.

<Script language="javascript">
function Fun1()
{
$.post("/Action1",null,CallBack1);
}
function Fun2()
{
$.post("/Action2",null,CallBack2);
}
</Script>

<form action="/Action1" method=post>
<input type=submit name=sub1 onclick="Fun2()"/>
</form>
<form action="/Action2" method=post>
<input type=submit name=sub2 onclick="Fun1()"/>
</form>

Использование "ActionNameSelectorAttribute": -

Это отличный и чистый вариант. "ActionNameSelectorAttribute" - это простой класс атрибутов, в котором мы можем написать логику принятия решений, которая решит, какое действие может быть выполнено.

Итак, первое, что нужно сделать в HTML, мы должны поместить собственные имена в кнопки отправки, чтобы идентифицировать их на сервере.

Вы можете видеть, что мы поставили "Сохранить" и "Удалить" на имена кнопок. Также вы можете заметить, что в действии мы только что установили имя контроллера "Клиент", а не конкретное имя действия. Мы ожидаем, что имя действия будет определяться с помощью "ActionNameSelectorAttribute".

<form action="Customer" method=post>
<input type=submit value="Save" name="Save" /> <br />
<input type=submit value="Delete" name="Delete"/>
</form>

Итак, когда нажата кнопка отправки, она сначала попадает в атрибут "ActionNameSelector", а затем в зависимости от того, какой из них отправлен, он вызывает соответствующее действие.

Изображение 2614

Итак, первым шагом является создание класса, который наследуется от класса ActionNameSelectorAttribute. В этом классе мы создали простое свойство "Имя" .

Нам также нужно переопределить функцию IsValidName, которая возвращает true или flase. Эта функция - это где мы пишем логику, должно ли действие быть выполнено или нет. Поэтому, если эта функция возвращает true, действие выполняется, иначе это не так.

public class SubmitButtonSelector : ActionNameSelectorAttribute
    {
        public string Name { get; set; }
        public override bool IsValidName(ControllerContext controllerContext, string actionName, System.Reflection.MethodInfo methodInfo)
        {
            // Try to find out if the name exists in the data sent from form
var value = controllerContext.Controller.ValueProvider.GetValue(Name);
            if (value != null)
            {
                return true;
            }
            return false;

        }
    }

Главное из вышеперечисленных функций приведено в приведенном ниже коде. Коллекция "ValueProvider" содержит все данные, которые были опубликованы из формы. Поэтому он сначала ищет значение "Имя" , и если он найден в HTTP-запросе, он возвращает true или возвращает false.

var value = controllerContext.Controller.ValueProvider.GetValue(Name);
if (value != null)
      {
        return true;
      }
      return false;

Этот класс атрибутов может быть затем оформлен в соответствующем действии и может быть предоставлено соответствующее значение "Имя" . Поэтому, если подача нажимает это действие и если имя совпадает с именем кнопки отправки HTML, оно выполняет действие дальше, иначе оно не выполняется.

public class CustomerController : Controller
{
        [SubmitButtonSelector(Name="Save")]
        public ActionResult Save()
        {
            return Content("Save Called");
        }
        [SubmitButtonSelector(Name = "Delete")]
        public ActionResult Delete()
        {
            return Content("Delete Called");
        }
}
  • 0
    Подробный и информативный ответ. Очень полезно.
6

Этот script позволяет указать атрибут формы данных, который будет работать как атрибут форматирования HTML5 во всех браузерах (ненавязчивым образом):

$(document).on('click', '[type="submit"][data-form-action]', function(event) {
    var $this = $(this),
    var formAction = $this.attr('data-form-action'),
    $form = $($this.closest('form'));
    $form.attr('action', formAction);             
});

Форма, содержащая кнопку, будет отправлена ​​в URL, указанный в атрибуте data-form-action:

<button type="submit" data-form-action="different/url">Submit</button>   

Для этого требуется jQuery 1.7. Для предыдущих версий вы должны использовать live() вместо on().

6

Если у вас нет ограничений на использование HTML 5, вы можете использовать тег <button> с formaction Атрибут:

<form action="demo_form.asp" method="get">
   First name: <input type="text" name="fname" /><br />
   Last name: <input type="text" name="lname" /><br />
   <button type="submit">Submit</button><br />
   <button type="submit" formaction="demo_admin.asp">Submit as admin</button>
</form>

Ссылка: http://www.w3schools.com/html5/att_button_formaction.asp

6

Это техника, которую я бы использовал, и я ее еще не вижу. Ссылка (размещена Saajid Ismail ), который вдохновляет это решение http://weblogs.asp.net/dfindley/archive/2009/05/31/asp-net-mvc-multiple-buttons-in-the-same-form.aspx). Он приспосабливает ответ Дилана Битти, чтобы сделать локализацию без проблем.

В представлении:

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<button name="button" value="send"><%: Resources.Messages.Send %></button>
<button name="button" value="cancel"><%: Resources.Messages.Cancel %></button>
<% Html.EndForm(); %>

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

public class MyController : Controller 
{
    public ActionResult MyAction(string button)
    {
         switch(button)
         {
             case "send":
                 this.DoSend();
                 break;
             case "cancel":
                 this.DoCancel();
                 break;
         }
    }
}
  • 0
    Похоже, решение Ironicnet предоставлено.
  • 0
    Конечно, похоже, но это показывает и локализацию, и код контроллера, чего я не видел в этом потоке. Я нашел эту ветку, когда искал, как это сделать, и хотел задокументировать, что я придумал для всех, кто может быть в той же лодке.
Показать ещё 1 комментарий
6

Дэвид Финдли пишет о трех разных вариантах, которые вы делаете для этого, в своем блоге ASP.Net.

Прочитайте статью несколько кнопок в той же форме, чтобы увидеть его решения, а также преимущества и недостатки каждого. IMHO он предлагает очень элегантное решение, которое использует атрибуты, которые вы украшаете своим действием.

5

У меня нет достаточного количества комментариев для комментариев в нужном месте, но я потратил весь день на это, поэтому хочу поделиться.

При попытке реализовать решение "MultipleButtonAttribute" ValueProvider.GetValue(keyValue) неправильно вернется null.

Оказалось, что я ссылался на System.Web.MVC версии 3.0, когда он должен был быть 4.0 (другие сборки - 4.0). Я не знаю, почему мой проект не обновился правильно, и у меня не было других очевидных проблем.

Итак, если ваш ActionNameSelectorAttribute не работает... проверьте это.

5

Здесь используется метод расширения, который я написал для обработки нескольких кнопок изображения и/или текста.

Здесь HTML для кнопки изображения:

<input id="btnJoin" name="Join" src="/content/images/buttons/btnJoin.png" 
       type="image">

или для кнопки отправки текста:

<input type="submit" class="ui-button green" name="Submit_Join" value="Add to cart"  />
<input type="submit" class="ui-button red" name="Submit_Skip" value="Not today"  />

Вот метод расширения, который вы вызываете из контроллера с помощью form.GetSubmitButtonName(). Для кнопок изображения он ищет параметр формы с .x (который указывает, что нажата кнопка изображения) и извлекает имя. Для обычных кнопок input он ищет имя, начинающееся с Submit_, и потом извлекает команду. Поскольку я абстрагирую логику определения "команды", вы можете переключаться между изображениями + текстовыми кнопками на клиенте, не меняя код на стороне сервера.

public static class FormCollectionExtensions
{
    public static string GetSubmitButtonName(this FormCollection formCollection)
    {
        return GetSubmitButtonName(formCollection, true);
    }

    public static string GetSubmitButtonName(this FormCollection formCollection, bool throwOnError)
    {
        var imageButton = formCollection.Keys.OfType<string>().Where(x => x.EndsWith(".x")).SingleOrDefault();
        var textButton = formCollection.Keys.OfType<string>().Where(x => x.StartsWith("Submit_")).SingleOrDefault();

        if (textButton != null)
        {
            return textButton.Substring("Submit_".Length);
        }

        // we got something like AddToCart.x
        if (imageButton != null)
        {
            return imageButton.Substring(0, imageButton.Length - 2);
        }

        if (throwOnError)
        {
            throw new ApplicationException("No button found");
        }
        else
        {
            return null;
        }
    }
}

Примечание.. Для текстовых кнопок вы должны префикс имени с помощью Submit_. Я предпочитаю этот способ, потому что это означает, что вы можете изменить значение текста (отображения), не меняя код. В отличие от элементов SELECT кнопка input имеет только "значение" и отдельный атрибут "текст". Мои кнопки говорят разные вещи в разных контекстах, но отображают одну и ту же команду. Я предпочитаю извлекать имя таким образом, чем код для == "Add to cart".

  • 0
    Мне нравится проверять имя как альтернативу, потому что вы не всегда можете проверить значение, например, для. у вас есть список элементов, и у каждого есть кнопка «Удалить».
4

Я очень опаздываю на вечеринку, но здесь идет... Моя реализация берет у @mkozicki, но требует меньше жестко заданных строк, чтобы ошибиться. Требуется Framework 4.5+. По сути, имя метода контроллера должно быть ключом к маршрутизации.

Разметка. Имя кнопки должно вводиться с помощью "action:[controllerMethodName]"

(обратите внимание на использование С# 6 nameof API, предоставляя привязку к конкретному типу имени имени метода контроллера, который вы хотите вызвать.

<form>
    ... form fields ....
    <button name="action:@nameof(MyApp.Controllers.MyController.FundDeathStar)" type="submit" formmethod="post">Fund Death Star</button>
    <button name="action:@nameof(MyApp.Controllers.MyController.HireBoba)" type="submit" formmethod="post">Hire Boba Fett</button>
</form>

контроллер

namespace MyApp.Controllers
{
    class MyController
    {    
        [SubmitActionToThisMethod]
        public async Task<ActionResult> FundDeathStar(ImperialModel model)
        {
            await TrainStormTroopers();
            return View();
        }

        [SubmitActionToThisMethod]
        public async Task<ActionResult> HireBoba(ImperialModel model)
        {
            await RepairSlave1();
            return View();
        }
    }
}

Магия атрибутов. Обратите внимание на использование CallerMemberName доброты.

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class SubmitActionToThisMethodAttribute : ActionNameSelectorAttribute
{        
    public SubmitActionToThisMethodAttribute([CallerMemberName]string ControllerMethodName = "")
    {
        controllerMethod = ControllerMethodName;
        actionFormat = string.Concat(actionConstant, ":", controllerMethod);
    }
    const string actionConstant = "action";
    readonly string actionFormat;
    readonly string controllerMethod;

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {
        var isValidName = false;
        var value = controllerContext.Controller.ValueProvider.GetValue(actionFormat);

        if (value != null)
        {
            controllerContext.Controller.ControllerContext.RouteData.Values[actionConstant] = controllerMethod;
            isValidName = true;
        }
        return isValidName;
    }
}
  • 0
    Хотя это хороший подход, вы не сможете использовать встроенный механизм связывания модели mvc, поскольку он использует атрибут «name» кнопок.
  • 0
    Цель этого решения - обойти пост-маршрутизацию MVC. Пожалуйста, опишите улучшение.
4

Если ваш браузер поддерживает форматирование атрибута для кнопок ввода (IE 10+, не уверен в других браузерах), то следующее должно работать:

@using (Html.BeginForm()){
    //put form inputs here

<input id="sendBtn" value="Send" type="submit" formaction="@Url.Action("Name Of Send Action")" />

<input id="cancelBtn" value="Cancel" type="submit" formaction="@Url.Action("Name of Cancel Action") />

}
  • 0
    Посмотрите на мой ответ ниже, он не зависит от черновиков спецификаций. Ваш ответ дает возможность иметь разные URL-адреса действий, а мои нет.
4

Модифицированная версия метода HttpParamActionAttribute, но с исправлением ошибки, не вызывающим ошибку при просроченных/недопустимых постсообщениях сеанса. Чтобы узнать, не является ли проблема с вашим текущим сайтом, откройте форму в окне и перед тем, как перейти к нажатию Save или Publish, откройте дублирующее окно и выйдите из системы. Теперь вернитесь в свое первое окно и попробуйте отправить свою форму с помощью любой кнопки. Для меня у меня есть ошибка, поэтому это изменение решает эту проблему для меня. Для краткости я опускаю кучу вещей, но вы должны получить эту идею. Ключевыми частями являются включение ActionName в атрибут и удостоверение, что имя, переданное в, является именем представления, которое показывает форму

Класс атрибута

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class HttpParamActionAttribute : ActionNameSelectorAttribute
{
    private readonly string actionName;

    public HttpParamActionAttribute(string actionName)
    {
        this.actionName = actionName;
    }

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {
        if (actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
            return true;

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

        var request = controllerContext.RequestContext.HttpContext.Request;
        return request[methodInfo.Name] != null;
    }
}

контроллер

[Authorize(Roles="CanAddContent")]
public ActionResult CreateContent(Guid contentOwnerId)
{
    var viewModel = new ContentViewModel
    {
        ContentOwnerId = contentOwnerId
        //populate rest of view model
    }
    return View("CreateContent", viewModel);
}

[Authorize(Roles="CanAddContent"), HttpPost, HttpParamAction("CreateContent"), ValidateAntiForgeryToken]
public ActionResult SaveDraft(ContentFormModel model)
{
    //Save as draft
    return RedirectToAction("CreateContent");
}

[Authorize(Roles="CanAddContent"), HttpPost, HttpParamAction("CreateContent"), ValidateAntiForgeryToken]
public ActionResult Publish(ContentFormModel model)
{
    //publish content
    return RedirectToAction("CreateContent");
}

Просмотр

@using (Ajax.BeginForm("CreateContent", "MyController", new { contentOwnerId = Model.ContentOwnerId }))
{
    @Html.AntiForgeryToken()
    @Html.HiddenFor(x => x.ContentOwnerId)

    <!-- Rest of your form controls -->
    <input name="SaveDraft" type="submit" value="SaveDraft" />
    <input name="Publish" type="submit" value="Publish" />
}
4

Я попытался синтезировать все решения и создал атрибут [ButtenHandler], который упрощает обработку нескольких кнопок в форме.

Я описал его на CodeProject Несколько кнопок с параметрами (локализуемыми) в ASP.NET MVC.

Чтобы справиться с простым случаем этой кнопки:

<button type="submit" name="AddDepartment">Add Department</button>

У вас будет что-то вроде следующего метода действий:

[ButtonHandler()]
public ActionResult AddDepartment(Company model)
{
    model.Departments.Add(new Department());
    return View(model);
}

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

4

это лучший способ, который я нашел:

http://iwayneo.blogspot.co.uk/2013/10/aspnet-mvc-action-selector-with-list.html

Вот код:

    /// <summary>
    /// ActionMethodSelector to enable submit buttons to execute specific action methods.
    /// </summary>
    public class AcceptParameterAttribute : ActionMethodSelectorAttribute
   {
        /// <summary>
        /// Gets or sets the value to use to inject the index into
        /// </summary>
       public string TargetArgument { get; set; }

       /// <summary>
       /// Gets or sets the value to use in submit button to identify which method to select. This must be unique in each controller.
       /// </summary>
       public string Action { get; set; }

       /// <summary>
       /// Gets or sets the regular expression to match the action.
       /// </summary>
       public string ActionRegex { get; set; }

       /// <summary>
       /// Determines whether the action method selection is valid for the specified controller context.
       /// </summary>
       /// <param name="controllerContext">The controller context.</param>
       /// <param name="methodInfo">Information about the action method.</param>
       /// <returns>true if the action method selection is valid for the specified controller context; otherwise, false.</returns>
       public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
       {

           if (controllerContext == null)
           {
               throw new ArgumentNullException("controllerContext");
           }

           Func<NameValueCollection> formGetter;
           Func<NameValueCollection> queryStringGetter;

           ValidationUtility.GetUnvalidatedCollections(HttpContext.Current, out formGetter, out queryStringGetter);

           var form = formGetter();
           var queryString = queryStringGetter();

           var req = form.AllKeys.Any() ? form : queryString;

           if (!string.IsNullOrEmpty(this.ActionRegex))
           {
               foreach (var key in req.AllKeys.Where(k => k.StartsWith(Action, true, System.Threading.Thread.CurrentThread.CurrentCulture)))
               {
                   if (key.Contains(":"))
                   {
                       if (key.Split(':').Count() == this.ActionRegex.Split(':').Count())
                       {
                           bool match = false;
                           for (int i = 0; i < key.Split(':').Count(); i++)
                           {
                               if (Regex.IsMatch(key.Split(':')[0], this.ActionRegex.Split(':')[0]))
                               {
                                   match = true;
                               }
                               else
                               {
                                   match = false;
                                   break;
                               }
                           }

                           if (match)
                           {
                               return !string.IsNullOrEmpty(req[key]);
                           }
                       }
                   }
                   else
                   {
                       if (Regex.IsMatch(key, this.Action + this.ActionRegex))
                       {
                           return !string.IsNullOrEmpty(req[key]);
                       }
                   }

               }
               return false;
           }
           else
           {
               return req.AllKeys.Contains(this.Action);
           }
       }
   }

Наслаждайтесь будущей передачей без кода.

Благодарю вас

3
//model
    public class input_element
        {
         public string Btn { get; set; }
        }   

//views--submit btn can be input type also...
    @using (Html.BeginForm())
    {
            <button type="submit" name="btn" value="verify">
             Verify data</button>
            <button type="submit" name="btn" value="save">
             Save data</button>    
            <button type="submit" name="btn" value="redirect">
                 Redirect</button>
    }

//controller

    public ActionResult About()
        {
            ViewBag.Message = "Your app description page.";
            return View();
        }

        [HttpPost]
        public ActionResult About(input_element model)
        {
                if (model.Btn == "verify")
                {
                // the Verify button was clicked
                }
                else if (model.Btn == "save")
                {
                // the Save button was clicked
                } 
                else if (model.Btn == "redirect")
                {
                // the Redirect button was clicked
                } 
                return View();
        }
  • 0
    Возможно, вы этого не поняли, но этот же ответ уже был опубликован довольно часто на этот вопрос.
  • 2
    Кроме того, вы, кажется, публикуете ответы, которые содержат только код, без объяснения причин. Не могли бы вы добавить какой-нибудь рассказ, чтобы объяснить, почему код работает, и что делает его ответом на вопрос? Это было бы очень полезно для человека, задающего вопрос, и для всех, кто придет.
Показать ещё 2 комментария
2

Основываясь на ответе mkozicki, я придумываю немного другое решение. Я все еще использую ActionNameSelectorAttribute Но мне нужно было обрабатывать две кнопки "Сохранить" и "Синхронизировать". Они делают почти то же самое, поэтому я не хотел иметь двух действий.

атрибут

public class MultipleButtonActionAttribute : ActionNameSelectorAttribute
{        
    private readonly List<string> AcceptedButtonNames;

    public MultipleButtonActionAttribute(params string[] acceptedButtonNames)
    {
        AcceptedButtonNames = acceptedButtonNames.ToList();
    }

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {            
        foreach (var acceptedButtonName in AcceptedButtonNames)
        {
            var button = controllerContext.Controller.ValueProvider.GetValue(acceptedButtonName);
            if (button == null)
            {
                continue;
            }                
            controllerContext.Controller.ControllerContext.RouteData.Values.Add("ButtonName", acceptedButtonName);
            return true;
        }
        return false;
    }
}

вид

<input type="submit" value="Save" name="Save" />
<input type="submit" value="Save and Sync" name="Sync" />

контроллер

 [MultipleButtonAction("Save", "Sync")]
 public ActionResult Sync(OrgSynchronizationEditModel model)
 {
     var btn = this.RouteData.Values["ButtonName"];

Я также хочу отметить, что если действия делают разные вещи, я, вероятно, последую за сообщением mkozicki.

2

Для каждой кнопки отправки просто добавьте:

$('#btnSelector').click(function () {

    $('form').attr('action', "/Your/Action/);
    $('form').submit();

});
2

Мой подход JQuery с использованием метода расширения:

public static MvcHtmlString SubmitButtonFor<TController>(this HtmlHelper helper, Expression<Action<TController>> action, string value) where TController : Controller
{
    RouteValueDictionary routingValues = Microsoft.Web.Mvc.Internal.ExpressionHelper.GetRouteValuesFromExpression(action);

    var onclick = string.Format("$('form').first().attr('action', '/{0}')", string.Join("/", routingValues.Values.ToArray().Where(x => x != null).Select(x => x.ToString()).ToArray()));
    var html = "<input type=\"submit\" value=\"" + value + "\" onclick=\"" + onclick + "\" />";

    return MvcHtmlString.Create(html);
}

Вы можете использовать его следующим образом:

@(Html.SubmitButtonFor<FooController>(c => c.Save(null), "Save"))

И это выглядит следующим образом:

<input type="submit" value="Save" onclick="$('form').first().attr('action', '/Foo/Save')" >
0

Используйте специальный помощник (создайте файл "Helpers.cshtml" внутри папки App_Code в корне вашего проекта) с javascript, чтобы переписать (при событии "onclick" ) атрибут формы "действие" для чего-то, что вы хотите и затем отправьте его.

Помощник может быть похож:

@helper SubmitButton(string text, string controller,string action)
{   
    var uh = new System.Web.Mvc.UrlHelper(Context.Request.RequestContext);
    string url = @uh.Action(action, controller, null);   
    <input type=button  onclick="(
                                       function(e)
                                                 {
                                                   $(e).parent().attr('action', '@url'); //rewrite action url
                                                   //create a submit button to be clicked and removed, so that onsubmit is triggered
                                                   var form = document.getElementById($(e).parent().attr('id'));
                                                   var button = form.ownerDocument.createElement('input');
                                                   button.style.display = 'none';
                                                   button.type = 'submit';
                                                   form.appendChild(button).click(); 
                                                   form.removeChild(button);              
                                                  }
                                      )(this)" value="@text"/>
}

И затем используйте его как:

@Helpers.SubmitButton("Text for 1st button","ControllerForButton1","ActionForButton1")
@Helpers.SubmitButton("Text for 2nd button","ControllerForButton2","ActionForButton2")
...

Внутри вашей формы.

0

Я создал метод ActionButton для HtmlHelper. Он будет генерировать нормальную кнопку ввода с немного javascript в событии OnClick, который отправит форму указанному контроллеру/действию.

Вы используете такой помощник

@Html.ActionButton("MyControllerName", "MyActionName", "button text")

это приведет к созданию следующего HTML

<input type="button" value="button text" onclick="this.form.action = '/MyWebsiteFolder/MyControllerName/MyActionName'; this.form.submit();">

Вот код метода расширения:

VB.Net

<System.Runtime.CompilerServices.Extension()>
Function ActionButton(pHtml As HtmlHelper, pAction As String, pController As String, pRouteValues As Object, pBtnValue As String, pBtnName As String, pBtnID As String) As MvcHtmlString
    Dim urlHelperForActionLink As UrlHelper
    Dim btnTagBuilder As TagBuilder

    Dim actionLink As String
    Dim onClickEventJavascript As String

    urlHelperForActionLink = New UrlHelper(pHtml.ViewContext.RequestContext)
    If pController <> "" Then
        actionLink = urlHelperForActionLink.Action(pAction, pController, pRouteValues)
    Else
        actionLink = urlHelperForActionLink.Action(pAction, pRouteValues)
    End If
    onClickEventJavascript = "this.form.action = '" & actionLink & "'; this.form.submit();"

    btnTagBuilder = New TagBuilder("input")
    btnTagBuilder.MergeAttribute("type", "button")

    btnTagBuilder.MergeAttribute("onClick", onClickEventJavascript)

    If pBtnValue <> "" Then btnTagBuilder.MergeAttribute("value", pBtnValue)
    If pBtnName <> "" Then btnTagBuilder.MergeAttribute("name", pBtnName)
    If pBtnID <> "" Then btnTagBuilder.MergeAttribute("id", pBtnID)

    Return MvcHtmlString.Create(btnTagBuilder.ToString(TagRenderMode.Normal))
End Function

С# (код С# просто декомпилируется из VB DLL, поэтому он может получить некоторое украшение... но время так короткое: -))

public static MvcHtmlString ActionButton(this HtmlHelper pHtml, string pAction, string pController, object pRouteValues, string pBtnValue, string pBtnName, string pBtnID)
{
    UrlHelper urlHelperForActionLink = new UrlHelper(pHtml.ViewContext.RequestContext);
    bool flag = Operators.CompareString(pController, "", true) != 0;
    string actionLink;
    if (flag)
    {
        actionLink = urlHelperForActionLink.Action(pAction, pController, System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(pRouteValues));
    }
    else
    {
        actionLink = urlHelperForActionLink.Action(pAction, System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(pRouteValues));
    }
    string onClickEventJavascript = "this.form.action = '" + actionLink + "'; this.form.submit();";
    TagBuilder btnTagBuilder = new TagBuilder("input");
    btnTagBuilder.MergeAttribute("type", "button");
    btnTagBuilder.MergeAttribute("onClick", onClickEventJavascript);
    flag = (Operators.CompareString(pBtnValue, "", true) != 0);
    if (flag)
    {
        btnTagBuilder.MergeAttribute("value", pBtnValue);
    }
    flag = (Operators.CompareString(pBtnName, "", true) != 0);
    if (flag)
    {
        btnTagBuilder.MergeAttribute("name", pBtnName);
    }
    flag = (Operators.CompareString(pBtnID, "", true) != 0);
    if (flag)
    {
        btnTagBuilder.MergeAttribute("id", pBtnID);
    }
    return MvcHtmlString.Create(btnTagBuilder.ToString(TagRenderMode.Normal));
}

Эти методы имеют различные параметры, но для удобства использования вы можете создать некоторую перегрузку, которая требует только необходимых параметров.

0

При использовании ajax-форм мы можем использовать ActionLinks с POST HttpMethod и сериализуйте форму в событии AjaxOptions.OnBegin.

Скажем, у вас есть два действия: InsertAction и UpdateAction:

<form>
    @Html.Hidden("SomeField", "SomeValue")

    @Ajax.ActionLink(
        "Insert",
        "InsertAction",
        null,
        new AjaxOptions { 
            OnBegin = "OnBegin", 
            UpdateTargetId = "yourDiv", 
            HttpMethod = "POST" })
    @Ajax.ActionLink(
        "Update",
        "UpdateAction",
        null,
        new AjaxOptions { 
            OnBegin = "OnBegin", 
            UpdateTargetId = "yourDiv", 
            HttpMethod = "POST" })
</form>

Javascript

function OnBegin(xhr, settings) {
    settings.data = $("form").serialize();
}
-2

В режиме просмотра код:

<script language="javascript" type="text/javascript">
    function SubmeterForm(id)
    {
        if (id=='btnOk')
            document.forms[0].submit(id);
        else
            document.forms[1].submit(id);
    }
</script>


<% Html.BeginForm("ObterNomeBotaoClicado/1", "Teste01", FormMethod.Post); %>
<input id="btnOk" type="button" value="Ok" onclick="javascript:SubmeterForm('btnOk')" />
<% Html.EndForm(); %>
<% Html.BeginForm("ObterNomeBotaoClicado/2", "Teste01", FormMethod.Post); %>
<input id="btnCancelar" type="button" value="Cancelar" onclick="javascript:SubmeterForm('btnCancelar')" />
<% Html.EndForm(); %>

В контроллере код:

public ActionResult ObterNomeBotaoClicado(string id)
{
    if (id=="1")
        btnOkFunction(...);
    else
        btnCancelarFunction(...);
    return View();
}
  • 0
    В будущем вам будет полезно объяснить ваш код. Спасибо!
-3

Мое решение состояло в том, чтобы использовать 2 панели asp:

<asp:Panel ID=".." DefaultButton="ID_OF_SHIPPING_SUBMIT_BUTTON"….></asp:Panel>

-4

Index.cshtml

@using (Html.BeginForm( "Index", "Home", FormMethod.Post, new {id = "submitForm" }))

{

button type = "submit" id = "btnApprove" name= Значение "Команда" = "Утвердить" > Утвердить

button type = "submit" id = "btnReject" name= Значение "Команда" = "Отклонить" > Отклонить

}

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Index(FormCollection frm, string Command)
    {
        if (Command == "Approve")
        {

        }
        else if (Command == "Reject")
        {

        }

        return View();
    }

}
  • 0
    это не работает

Ещё вопросы

Сообщество Overcoder
Наверх
Меню