ASP.NET MVCフレームワークで複数の送信ボタンをどのように処理しますか?


733

同じフォームから複数の送信ボタンを処理する簡単な方法はありますか?例えば:

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

ASP.NET Framework Betaでこれを行う方法はありますか?私がグーグルで検索したすべての例には、1つのボタンがあります。


6
ASP.NET Coreから始めて言及する価値があるのは、ここにリストされているものよりもはるかに簡単なソリューションです。
スティーブンジュリス2017

このチュートリアルは役立つかもしれません:ASP.NET MVCのさまざまなアクションメソッドにフォームを
送信する

回答:


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

更新: Razorページは、そのままの状態で同じ機能を提供するように見えます。新規開発の場合、それが望ましいかもしれません。


3
私はこの解決策が、使用されている他のテクニックの幸せな結婚であることがわかりました。完全に動作し、ローカリゼーションには影響しません。
trevorc 2011年

17
素晴らしいソリューション!! スコアの高い回答よりもはるかに有用である
Sasha

11
このアプローチの問題return View(viewmodel)は、モデルにエラーがある場合に呼び出そうとすると、呼び出されたビューを返すSendか、引数名が何であるかによって返されることです。

15
@Shoe-同様のものを見つけたところです。あなたが明示的にこの方法を使用している場合に返すビューの名前を指定することを確認しますreturn View("Index", viewModel)
ajbeaven

4
情報のみです。system.Reflectionfor 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 2010年

10
<buttonタイプの値はテキストではないので、<inputタイプの代わりに<button type = "submit"を使用する必要があります;)。次に、次のようなものを作成できます。<button name = "mySubmitButton" type = "submit" value = "keyValue"> YourButtonText </ button>
J4N

4
これは、送信値だけでなくアクションにモデルを渡すことでどのように機能しますか?
bizzehdee 2013

2
jQueryを使用している場合は、ボタンに「アクション」という名前を付けないように注意してください。ライブラリ内で競合が発生し、アクションURLが壊れます。
HotN、2014年

121

前述のようにアクションで名前を確認できますが、これが適切な設計であるかどうかを検討する場合があります。アクションの責任を考慮し、このデザインをボタン名などのUIの側面にあまり結び付けないことをお勧めします。したがって、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
これは、すべての送信ボタンに同じフォームデータが必要ない場合に問題ありません。すべてのデータが共通の形式で必要な場合は、Dylan Beattieが適しています。これを行うためのよりエレガントな方法はありますか?
ジダン2009

3
ビジュアルプレゼンテーションについて、この場合、[キャンセル]ボタンの横にある[送信]ボタンはどのようになっていますか?
Kris-I

1
ディラン:キャンセルボタンの場合、データを送信する必要はまったくありません。コントローラーをUI要素に結合することはお勧めできません。ただし、多かれ少なかれ一般的な「コマンド」を作成できる場合は問題ないと思いますが、「submitButton」には関連付けません。これはUI要素の名前であるためです。
Trevor de Koekkoek 2011年

1
@クリス:CSSを使用してボタンを配置できますが、ボタンは2つの異なるフォームセクションに配置できます。
Trevor de Koekkoek 2011年

8
真剣に?それは私以外の誰にも匂いがしませんか?!

99

Eilonは、次のようにすることをお勧めします。

ボタンが複数ある場合は、各ボタンに名前を付けることでボタンを区別できます。

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

いずれかの値がこれらのパラメーターの1つにポストされた場合、そのボタンがクリックされたボタンでした。Webブラウザーは、クリックされた1つのボタンの値のみを送信します。他のすべての値はnullになります。

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を気にしない場合は、value属性を指定して<button>を使用します。
Kugel 2013年

このフォームでajax呼び出しを作成する場合、どのようにしますか。form.serialize()がないピックアップアップボタン名を提出しないようだ...
MKO

@Kugelは正しいです、これはまだ最もクリーンな答えです。ありがとう
Arif YILMAZ

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

と2つのメソッドを持つコントローラ

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

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

ご覧のとおり、この属性では何も指定する必要がありません。また、ボタンの名前はメソッド名に直接変換されます。さらに(私は試していませんが)これらは通常のアクションとしても機能するので、直接それらに投稿できます。


1
綺麗な!これが最もエレガントなソリューションだと思います。これは、タグのsubmit考慮から除外します。これは、制御フローに影響を与えないはずのpure-UI属性であるため、理想的です。代わりに、namesubmitタグの一意の属性は、コントローラーの個別のアクションメソッドに直接変換されます。
カーク・ウォル

+1私にとって、この問題の解決策は断然最高です。実装したので、多くのトラフィックがHttpParamActionAttributを通過することに気づきましたが、Asp.Net MVCがリクエストの処理中に実行する必要がある他のすべてのものと比較すると、それは完全に受け入れ可能です。ハックするだけの場合は、コントローラに名前の付いた空の「アクション」を配置して、Resharperがアクション「アクション」が存在しないことを警告しないようにします。どうもありがとうございました!
Samuel

私はすべてのソリューションを確認しましたが、これもエレガントでシンプルな優れたソリューションであることに同意しました。素晴らしいbc条件付きのステートメントはなく、新しいボタンがあるときに新しいコントローラーアクションを定義できる堅牢です。私のクラスをMultiButtonActionHandler FYIと呼びました;-)
ejhost 2015

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

トップの回答よりもはるかに簡単です!ありがとう!
pabben 2018年

35

利害関係者にMaarten Balliauwのソリューションを見てもらうことをお勧めします。とても上品だと思います。

リンクが消えた場合はMultiButton、コントローラーアクションに適用された属性を使用して、そのアクションに関連するボタンクリックを示します。


これは、現在使用しているソリューションであり、非常にきちんとしています。MVC 2だけですか?
Simonは2010年

これは美しいです!私はこれを見たことがない!複数の送信を使用して1つのボタンのみを使用するソリューションを再設計する場合があることに同意しますが、私は困っているため、これを行う必要があります。この答えは勝ったはずです!
リコン、2011

これは素晴らしいソリューションです。とてもきれい
Arnej65

このアプローチを試したところ、MVC3で機能させることができませんでした。#1投票ゲッターのバリエーションが私のために働いた。
Scott Lawrence、

短くて甘い..しかし、mvc 3+には適していません
Piotr Kula

21

ボタンに名前を付けて値を与えることができるはずです。次に、この名前を引数としてアクションにマッピングします。または、2つの個別のアクションリンクまたは2つのフォームを使用します。


私が見てきた中で、これまでで最もクリーンで簡単なソリューションです。
Jason Evans、

13

あなたは書くことができます:

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

次に、ページで、名前== "送信"または名前== "キャンセル"かどうかを確認します...


1
これは機能していますが、同じ名前の2つの要素を使用するのは間違っていると思います。
ペーテル

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にnull値を取得します。なぜなのかご存知ですか?
Diganta Kumar 2013

対応するボタンをクリックすると、どちらもnullになりません。どのボタンをクリックしてヌルを取得しますか?
セルゲイ2013

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

    }

}

これは、MSP内でASP.NET WebForms機能が必要な単純なWebアプリに使用することを選択したソリューションです。
BrandonG 2016

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

お使いのブラウザーが入力ボタンの属性formaction(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を持つ可能性を可能にします。
Tom Hofman、2014

9

上記の問題を解決する方法は3つあります

  1. HTMLの方法
  2. jqueryの方法
  3. 「ActionNameSelectorAttribute」の方法

以下は、3つのアプローチすべてを実証的に要約したビデオです。

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

HTMLの方法:-

HTMLの方法では、2つのフォームを作成し、各フォーム内に「送信」ボタンを配置する必要があります。そして、すべてのフォームのアクションは、異なる/それぞれのアクションを指します。以下のコードを見ると、最初のフォームが「Action1」に投稿されており、2番目のフォームは、「送信」ボタンがクリックされたことに応じて「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愛好家である場合、この2番目のオプションはあなたをさらに興奮させます。Ajaxの方法では、2つの異なる関数「Fun1」と「Fun1」を作成できます。以下のコードを参照してください。これらの関数は、JQUERYまたはその他のフレームワークを使用してAjax呼び出しを行います。これらの各機能は、「送信」ボタンの「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で、サーバー上でボタンを識別するために送信ボタンに適切な名前を付ける必要があります。

ボタンの名前に「保存」と「削除」を追加したことがわかります。また、特定のアクション名ではなく、コントローラー名「Customer」を付けただけのアクションに気付くでしょう。アクション名は「ActionNameSelectorAttribute」によって決定されると予想されます。

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

そのため、送信ボタンがクリックされると、最初に「ActionNameSelector」属性がヒットし、次にどの送信が起動されるかに応じて、適切なアクションが呼び出されます。

ここに画像の説明を入力してください

したがって、最初のステップは、「ActionNameSelectorAttribute」クラスから継承するクラスを作成することです。このクラスでは、単純なプロパティ「Name」を作成しました。

また、trueまたはflaseを返す「IsValidName」関数をオーバーライドする必要があります。この関数は、アクションを実行する必要があるかどうかに関係なく、ロジックを書き込む場所です。したがって、この関数がtrueを返す場合、アクションが実行されるか、そうでない場合です。

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

        }
    }

上記の関数の中心は、以下のコードにあります。「ValueProvider」コレクションには、フォームから投稿されたすべてのデータが含まれます。したがって、最初に「名前」の値を検索し、HTTPリクエストで見つかった場合はtrueを返し、そうでない場合はfalseを返します。

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

次に、この属性クラスをそれぞれのアクションに装飾し、それぞれの「名前」値を指定できます。そのため、送信がこのアクションにヒットし、名前がHTML送信ボタンの名前と一致する場合は、アクションがさらに実行されるか、そうでない場合に実行されます。

public class CustomerController : Controller
{
        [SubmitButtonSelector(Name="Save")]
        public ActionResult Save()
        {
            return Content("Save Called");
        }
        [SubmitButtonSelector(Name = "Delete")]
        public ActionResult Delete()
        {
            return Content("Delete Called");
        }
}

7

David Findleyは、ASP.Netウェブログで、これを行うための3つの異なるオプションについて書いています。

同じフォームの複数のボタンの記事を読んで、彼のソリューションと、それぞれの利点と欠点を確認してください。私見彼はあなたがあなたの行動を飾る属性を利用する非常にエレガントなソリューションを提供します。


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が提供するソリューションのように見えます。
クリスファンデルマスト2010年

確かに似ていますが、これはローカリゼーションとコントローラーコードの両方を示しています。これを行う方法を探しているときにこのスレッドを見つけ、同じボートに乗っている可能性のある他の人のために私が思いついたものを文書化したいと思いました。
mlibby 2010年

1
実際、それはそれ以上のIronicnetのものと同じではありません。彼は<input>要素を使用しています。私はを使用します<button>。これは、変数値属性を持たずにローカライズを行うために必要です。
mlibby 2010年

7

このスクリプトでは、すべてのブラウザーでHTML5 formaction属性として機能するdata-form-action属性を指定できます(控えめな方法で)。

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

ボタンを含むフォームは、data-form-action属性で指定されたURLに送信されます。

<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

それが4.0でなければならないときにSystem.Web.MVCバージョン3.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 2016年

このソリューションの目的は、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>

    }

これは、フォーム投稿のさまざまな方法を説明する便利なリンクです。
Himalaya Garg、

5

私はすべてのソリューションを統合して、フォーム上の複数のボタンを簡単に処理できるようにする[ButtenHandler]属性を作成しました。

ASP.NET MVCの CodeProject 複数のパラメーター化(ローカライズ可能)フォームボタンについて説明しました。

このボタンの単純なケースを処理するには:

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

あなたはそれを理解していないかもしれませんが、この同じ答えがすでにこの質問にかなりの回数投稿されていました。
Andrew Barber

2
また、説明がなく、コードのみを含む回答を投稿しているようです。コードが機能する理由と、それが質問の答えになる理由を説明するために、説明を追加することを検討しますか?これは、質問をする人や、一緒に来た人にとって非常に役立ちます。
Andrew Barber

2
もちろん、問題なく動作します。しかし、その答えはずっと前にすでに他の人から与えられいます。また、なぜ機能するのについての説明も含まれています。
Andrew Barber

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/...
イアン・ケンプ

こんにちはイアン-そのアウトを掘りに感謝-私はそれをここに転載しました: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ますが、「保存」と「同期」の2つのボタンを処理する必要がありました。彼らはほとんど同じことをするので、私は2つのアクションを持ちたくありませんでした。

属性

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

HtmlHelperのActionButtonメソッドを作成しました。これは、フォームを指定されたコントローラー/アクションに送信するOnClickイベントで、javascriptのビットを含む通常の入力ボタンを生成します

あなたはそのようなヘルパーを使います

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

これらのメソッドにはさまざまなパラメーターがありますが、使いやすくするために、必要なパラメーターのみを取るオーバーロードを作成できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.