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


734

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

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

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


6
Стоит отметить, что начиная с ASP.NET Core , решения гораздо проще, чем перечисленные здесь.
Стивен Джеурис

Ответы:


629

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

[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 страниц выглядит так, чтобы обеспечить ту же функциональность из коробки. Для новых разработок это может быть предпочтительнее.


3
Я нашел это решение счастливым браком других методов, используемых. Работает отлично и не влияет на локализацию.
Trevorc

17
Отличное решение !! Намного полезнее, чем ответы с самыми высокими оценками
Саша

11
Проблема с этим подходом состоит в том, что если вы попытаетесь return View(viewmodel)в случае, когда в вашей модели есть ошибки, она попытается вернуть вызванное представление Sendили в зависимости от того, как называется ваш аргумент.
Чистка

15
@Shoe - только что нашли похожую вещь. Убедитесь, что вы явно указали имя представления, которое будет возвращено, если вы используете этот метод:return View("Index", viewModel)
ajbeaven

4
просто информация, нам нужно добавить system.Reflection для MethodInfo
Vignesh Subramanian

469

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

<% 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"));
    }

}

РЕДАКТИРОВАТЬ:

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

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

<% 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());
}

28
Жаль, что вы зависите от текста, отображаемого на кнопке, это довольно сложно с мультиязычным пользовательским интерфейсом
Omu

3
Переключатель / регистр работает только с константами, поэтому в локализованной версии нельзя использовать переключатель / регистр. Вам нужно переключиться на if else или какой-либо другой способ отправки.
mlibby

10
вы должны использовать <button type = "submit" вместо <input type, потому что значение типа <button не является текстом;). Тогда у вас может быть что-то вроде этого: <button name = "mySubmitButton" type = "submit" value = "keyValue"> YourButtonText </ button>
J4N

4
Как это будет работать с передачей модели действию, а не просто значением отправки?
bizzehdee

2
Будьте осторожны, чтобы не называть ваши кнопки «action», если вы используете jQuery. Это вызывает конфликт внутри библиотеки, который нарушает действие URL.
HotN

121

Вы можете проверить имя в действии, как было упомянуто, но вы можете решить, является ли это хорошим дизайном. Хорошая идея - рассмотреть ответственность за действие и не связывать этот дизайн слишком сильно с такими аспектами пользовательского интерфейса, как названия кнопок. Итак, рассмотрим использование 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") %>

53
Это нормально, когда вам не нужны одинаковые данные формы для каждой кнопки отправки. Если вам нужны все данные в общей форме, то Дилан Битти - это то, что вам нужно. Есть ли более элегантный способ сделать это?
Зидан

3
Что касается визуального представления, как в этом случае кнопка «Отправить» рядом с кнопкой «Отмена»?
Крис-I

1
Дилан: Хорошо, для кнопки отмены вам вообще не нужно отправлять данные, и это плохая практика - связывать контроллер с элементами пользовательского интерфейса. Однако, если вы можете сделать более или менее общую «команду», то я думаю, что это нормально, но я бы не стал связывать ее с «submitButton», так как это имя элемента пользовательского интерфейса.
Тревор де Коеккук

1
@ Kris: вы можете расположить свои кнопки с помощью CSS, и они все еще могут находиться в 2 разных разделах формы.
Тревор де Коеккук

8
шутки в сторону? это не пахнет ни для кого, кроме меня ?!

99

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

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

<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 */ }

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

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


2
Если кто-то сталкивается с этим старым вопросом, это самый чистый ответ, если вы не хотите использовать элементы HTML5 <button>. Если вы не возражаете против HTML5, используйте <button> с атрибутом value.
Кугель

Как бы вы это сделали, если создали ajax-вызов в этой форме. Похоже, что form.serialize () не отвечает за отправку имени кнопки.
mko

@ Кугель прав, это самый чистый ответ. Спасибо
Ариф ЙИЛМАЗ

45

Только что написал пост об этом: несколько кнопок отправки с 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тега из рассмотрения, который является идеальным , поскольку он является атрибутом чистого пользовательского интерфейса , который не должен иметь никакого отношения к потоку управления. Вместо этого уникальный nameатрибут каждого submitтега преобразуется непосредственно в метод дискретного действия на вашем контроллере.
Кирк Волл

+1 Для меня это, безусловно, лучшее решение этой проблемы. С тех пор как я его реализовал, я заметил, что через HttpParamActionAttribut проходит много трафика, но по сравнению со всеми другими вещами, которые Asp.Net MVC должен делать при обработке запроса, это вполне приемлемо. Чтобы взломать только то, что мне нужно сделать, это поместить пустое «Действие» с именем в моем контроллере, чтобы Resharper не предупредил меня, что действие «Действие» не существует. Большое спасибо!
Самуил

Я рассмотрел все решения и также согласен, что это хорошее элегантное, простое решение. Замечательно, что нет никаких условных операторов и надежно, где вы можете определить новое действие контроллера, когда у вас есть новая кнопка. Называется мой класс MultiButtonActionHandler FYI ;-)
ejhost

36

это короткий и набор:

На это ответил 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
}

Удачи.


Потрясающие. именно то, что я делал в веб-формах. Приветствия приятель
djack109

Гораздо проще, чем главный ответ! Спасибо!
Паббен

35

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

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


Это решение, которое мы используем сейчас, и оно очень аккуратное. Это только MVC 2?
Саймон

Это прекрасно! Я не видел этого раньше! Хотя я согласен с тем, что вы, возможно, захотите изменить дизайн любого решения, использующего несколько отправок, чтобы использовать только одну кнопку, я нахожусь в таком положении, что у меня возникают проблемы, и я должен это сделать. Этот ответ должен был победить!
Рикон

Это отличное решение. Очень чистый
Arnej65

Опробовал этот подход и не смог заставить его работать в MVC3. Вариант # 1 получателя голосов работал на меня.
Скотт Лоуренс

Коротко и сладко .. но не для мвк 3+
Петр Кула

21

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


Безусловно самое чистое и простое решение, которое я видел.
Джейсон Эванс

13

Вы могли бы написать:

<% 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
Ironicnet

12

Что мне не нравится в 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" />

11

Вот что работает лучше всего для меня:

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

Я получаю нулевое значение для onDelete и onSave в методе контроллера. Ты знаешь почему?
Диганта Кумар

Любой из них не будет нулевым, если вы нажмете соответствующую кнопку. На какую кнопку вы нажимаете, чтобы получить ноль?
Сергей

11

Я также столкнулся с этой «проблемой», но нашел довольно логичное решение, добавив 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

    }

}

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

10

Если у вас нет ограничений на использование 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


9

Если ваш браузер поддерживает формирование атрибутов для кнопок ввода (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") />

}

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

9

Есть три способа решения вышеуказанной проблемы.

  1. HTML способ
  2. Jquery способ
  3. Способ «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», а затем, в зависимости от того, какая отправка запущена, вызывает соответствующее действие.

введите описание изображения здесь

Поэтому первым шагом является создание класса, который наследуется от класса «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-запросе, возвращает значение «истина» или возвращает «ложь».

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

7

Дэвид Финдли (David Findley) пишет о 3 различных вариантах для этого в своем блоге ASP.Net.

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


7

Это техника, которую я бы использовал, и я пока не вижу ее здесь. Ссылка (опубликованная 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;
         }
    }
}

Похоже, решение Ironicnet предоставлено.
Крис ван дер Маст

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

1
На самом деле, это не то же самое, что Ironicnet. Он использует <input>элементы. Я использую <button>, что требуется, чтобы сделать локализацию без атрибутов значения переменной.
mlibby

7

Этот скрипт позволяет указать атрибут data-form-action, который будет работать как атрибут формирования 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-код для кнопки с изображением:

<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_и извлекает команду после. Поскольку я абстрагируюсь от логики определения «команды», вы можете переключаться между кнопками image + text на клиенте, не меняя код на стороне сервера.

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".


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

6

У меня недостаточно представителей, чтобы комментировать в правильном месте, но я потратил на это целый день, поэтому хочу поделиться.

При попытке реализовать решение «MultipleButtonAttribute» ValueProvider.GetValue(keyValue)будет возвращаться неправильно null.

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

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


6

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

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

(обратите внимание на использование C # 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;
    }
}

Хотя это хороший подход, вы не сможете использовать встроенный механизм связывания модели mvc, поскольку он использует атрибут «имя» кнопок.
ooXei1sh

Цель этого решения - обойти пост-маршрутизацию MVC. Пожалуйста, опишите улучшение.
Аарон Худон

6
[HttpPost]
public ActionResult ConfirmMobile(string nameValueResend, string nameValueSubmit, RegisterModel model)
    {
        var button = nameValueResend ?? nameValueSubmit;
        if (button == "Resend")
        {

        }
        else
        {

        }
    }


    Razor file Content:
    @using (Html.BeginForm()
    {
        <div class="page registration-result-page">

            <div class="page-title">
                <h1> Confirm Mobile Number</h1>
            </div>

            <div class="result">
                @Html.EditorFor(model => model.VefificationCode)
                @Html.LabelFor(model => model.VefificationCode, new { })
                @Html.ValidationMessageFor(model => model.VefificationCode)
            </div>
            <div class="buttons">
                <button type="submit" class="btn" name="nameValueResend" value="Resend">
                    Resend
                </button>
                <button type="submit" class="btn" name="nameValueSubmit" value="Verify">
                    Submit
                </button>

            </div>
            </div>

    }

Это еще одна полезная ссылка, объясняющая различные способы отправки формы.
Гималаи

5

Я попытался объединить все решения и создал атрибут [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);
}

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


5
//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();
        }

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

2
Кроме того, вы, кажется, публикуете ответы, которые содержат только код, без объяснения причин. Не могли бы вы добавить какой-нибудь рассказ, чтобы объяснить, почему код работает, и что делает его ответом на вопрос? Это было бы очень полезно для человека, задающего вопрос, и для всех, кто придет.
Эндрю Барбер

2
Конечно, работает нормально. Но этот ответ уже был дан другими , давным-давно. И они включали объяснения, почему это тоже работает.
Эндрю Барбер

5

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

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

Наслаждайтесь несколькими кнопками отправки без запаха.

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


Ссылка на данный момент не работает, это самая близкая заархивированная версия, которую я смог найти: web.archive.org/web/20110706230408/http://blogs.sonatribe.com/…
Ian Kemp

Привет Йен - спасибо за то, что выкопали это - я разместил это здесь: iwayneo.blogspot.co.uk/2013/10/…

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" />
}

3

Мой подход 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')" >

3

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

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

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

});

3

На основании ответа 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.


1

Я создал метод 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

C # (код C # просто декомпилируется из 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));
}

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.