Як ви обробляєте кілька кнопок подання в рамках ASP.NET MVC Framework?


733

Чи є якийсь простий спосіб обробляти кілька кнопок подання з однієї форми? Наприклад:

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

Будь-яка ідея, як це зробити в ASP.NET Framework Beta? Усі приклади, які я шукаю в Google, мають в них поодинокі кнопки.


6
Варто згадати, починаючи з ASP.NET Core , є набагато простіші рішення, ніж ті, що перераховані тут.
Стівен Євріс

Відповіді:


629

Ось здебільшого чисте рішення, засноване на атрибутах, щодо проблеми з декількома кнопками подання, що базується на публікації та коментарях Maarten 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
Я знайшов це рішення щасливим шлюбом з інших застосованих методик. Працює ідеально і не впливає на локалізацію.
trevorc

17
Чудове рішення !! Набагато корисніше, ніж відповіді, які отримали найкращий результат
Саша

11
Проблема такого підходу полягає в тому, що якщо ви намагаєтесь return View(viewmodel)у випадку, коли у вашої моделі є помилки, вона спробує повернути вигляд, викликаний Sendабо залежно від того, яке ім'я вашого аргументу.
взуття

15
@Shoe - щойно знайшли подібну річ. Переконайтесь, що ви чітко вказуєте ім'я перегляду, яке потрібно повернути, якщо ви використовуєте цей метод:return View("Index", viewModel)
ajbeaven

4
лише інформацію, нам потрібно додати system.Рефлексія для 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
занадто погано, що ви залежаєте від тексту, який відображається на кнопці, це начебто хитро з багатомовним інтерфейсом користувача
Ому,

3
Switch / case працює лише з константами, тому локалізована версія не може використовувати перемикач / регістр. Вам потрібно перейти на інший або інший метод відправки.
mlibby

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

4
Як би це працювало з передачею моделі дії, а не лише значенням подання?
bizzehdee

2
Будьте обережні, щоб не називати свої кнопки "дією", якщо ви використовуєте 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
Щодо візуальної презентації, як у такому випадку мати кнопку "Надіслати" поруч із кнопкою "Скасувати"?
Кріс-І

1
Ділан: Ну а для кнопки скасування вам взагалі не потрібно надсилати дані, і погана практика з'єднувати контролер з елементами інтерфейсу користувача. Однак якщо ви можете зробити більш-менш загальну "команду", то я думаю, що це нормально, але я б не прив'язував її до "submitButton", оскільки це ім'я елемента інтерфейсу користувача.
Тревор де Коеккьок

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

8
серйозно? це не пахне нікому, крім мене ?!

98

Ейлон пропонує зробити це так:

Якщо у вас є декілька кнопок, ви можете їх розрізнити, вказавши ім’я кожної кнопки:

<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

@Kugel має рацію, це все ще найчистіша відповідь. Спасибі
Аріф ЙІЛМАЗ

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 під час обробки запиту, це цілком прийнятно. Щоб зламати лише те, що мені потрібно зробити, це поставити порожній "Action", названий у моєму контролері, щоб запобігти попередженню Resharper, що дія "Action" не існує. Дуже дякую!
Самуїл

Я переглянув усі рішення, а також погоджуюся, що це приємне елегантне, просте рішення. У великому bc немає умовних заяв і надійних, де ви можете визначити нову дію контролера, коли у вас є нова кнопка. Зателефонував моєму класу MultiButtonActionHandler FYI ;-)
ejhost

36

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

На це відповів Джероен Доп

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

і робити так у коді behinde

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

Удачі.


Дивовижно. саме те, що я робив у веб-формах. Ура, приятель
djack109

Набагато простіше, ніж головна відповідь! Дякую!
pabben

35

Я б запропонував зацікавленим сторонам ознайомитись із рішенням Маартена Балліо . Я думаю, що це дуже елегантно.

Якщо посилання припиняється, то за допомогою MultiButtonатрибута, застосованого до дії контролера , використовується атрибут, який вказує, до якого клацання кнопки ця дія має стосуватися.


Це рішення, яке ми зараз використовуємо, і дуже акуратне. Це лише MVC 2?
Саймон Тримай

Це прекрасно! Я цього раніше не бачив! Хоча я погоджуюся з тим, що ви можете переробити будь-яке рішення, яке використовує декілька подань, щоб використовувати лише одну кнопку, я перебуваю в місці, де я перебуваю, і мушу це зробити. Ця відповідь мала виграти!
Рікон

Це чудове рішення. Дуже чисто
Arnej65

Спробував цей підхід і не зміг змусити його працювати в MVC3. Варіант №1, який отримав голос, працював на мене.
Скотт Лоуренс

Короткий і солодкий .. але не для mvc 3+
Piotr Kula

21

Ви повинні мати можливість назвати кнопки та надати їм значення; потім зіставіть це ім'я як аргумент дії. Крім того, використовуйте 2 окремих посилання-дії або 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;
            }
        }
    }
}

Фільтр знайде назву кнопки в даних форми, і якщо назва кнопки відповідає будь-якому з типів кнопок, визначених в enum, він знайде параметр 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 способом ми можемо створити дві різні функції «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. Ця функція полягає в тому, коли ми пишемо логіку, чи потрібно виконати дію чи ні. Отже, якщо ця функція повертає істину, тоді дія виконується, або це не так.

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

Девід Фіндлі пише про три різні варіанти для цього у своєму веб-журналі ASP.Net.

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


7

Це я техніка, яку я використовував, і я її поки не бачу. Посилання (розміщене Сааід Ісмаїлом), яке надихає на це рішення, 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.
Kris van der Mast

Безумовно, схоже, але це показує і локалізацію, і код контролера. Це те, що я не бачив зробити це в цій темі. Я знайшов цю тему, шукаючи, як це зробити, і хотів задокументувати те, що я придумав для всіх інших, хто може бути в одному човні.
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_і витягує команду згодом. Оскільки я абстрагуюсь від логіки визначення "команди", ви можете перемикатися між зображеннями та текстовими кнопками на клієнті, не змінюючи код сторони сервера.

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, але вимагає менш жорстких рядків, щоб помилитися. Необхідна рамка 4.5+ . По суті, назва методу контролера має бути ключовим для маршрутизації.

Розмітка . Ім'я кнопки повинно бути введено клавішею"action:[controllerMethodName]"

(зауважте використання API name # Cof 6 , надаючи специфічне посилання на ім'я методу контролера, який ви хочете викликати.

<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

Привіт Іне - спасибі, що викопав це - я відправив

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 # просто декомпілюється з DLL VB, тому він може отримати трохи прикраси ... але час такий короткий :-))

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.