同じフォームから複数の送信ボタンを処理する簡単な方法はありますか?例:
<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" value="Send" />
<input type="submit" value="Cancel" />
<% Html.EndForm(); %>
どのようにASP.NET Framework Betaでこれを行うのですか?私がグーグルしたすべての例はそれらに単一のボタンがあります。
これは、投稿と Maartin Balliauw からのコメントに重点を置いた、複数の送信ボタンの問題に対する、ほとんどクリーンな属性ベースの解決策です。
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class MultipleButtonAttribute : ActionNameSelectorAttribute
{
public string Name { get; set; }
public string Argument { get; set; }
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
{
var isValidName = false;
var keyValue = string.Format("{0}:{1}", Name, Argument);
var value = controllerContext.Controller.ValueProvider.GetValue(keyValue);
if (value != null)
{
controllerContext.Controller.ControllerContext.RouteData.Values[Name] = Argument;
isValidName = true;
}
return isValidName;
}
}
かみそり:
<form action="" method="post">
<input type="submit" value="Save" name="action:Save" />
<input type="submit" value="Cancel" name="action:Cancel" />
</form>
とコントローラ:
[HttpPost]
[MultipleButton(Name = "action", Argument = "Save")]
public ActionResult Save(MessageModel mm) { ... }
[HttpPost]
[MultipleButton(Name = "action", Argument = "Cancel")]
public ActionResult Cancel(MessageModel mm) { ... }
更新: かみそりのページ は箱から出して同じ機能を提供するように見えます。新規開発のためには、それが好ましいかもしれません。
送信ボタンに名前を付けて、送信した値をコントローラのメソッドで調べます。
<% 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());
}
前述のようにアクションで名前を確認することができますが、これが良いデザインであるかどうかを検討する必要があります。アクションの責任を考慮し、このデザインとボタン名のような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") %>
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ブラウザはクリックされた one ボタンの値のみを投稿します。他のすべての値はNULLになります。
if (saveButton != null) { /* do save logic */ } if (cancelButton != null) { /* do cancel logic */ }
割り当てられた名前よりも変更される可能性が高く、JavaScriptを有効にする必要がないので、送信ボタンのvalueプロパティに依存しないので、この方法が好きです。
参照してください: http://forums.asp.net/p/1369617/2865166.aspx#2865166
ちょうどそれについての記事を書いた: 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(…) {
//…
}
}
お分かりのように、この属性では何も指定する必要がありません。また、ボタンの名前はメソッド名に直接変換されます。さらに(私は試したことはありません)これらは通常のアクションとしても機能するはずなので、あなたはそれらのどれにでも直接投稿することができます。
利害関係者に提案します - Maarten Balliauwの解決策を見てください - /。とてもエレガントだと思います。
リンクが消えた場合は、コントローラのアクションに適用されたMultiButton
属性を使用して、そのアクションがどのボタンクリックに関連するかを示します。
それは短くてスイートです:
Jeroen Dop と回答しました
<input type="submit" name="submitbutton1" value="submit1" />
<input type="submit" name="submitbutton2" value="submit2" />
そしてコードbehindeでこのようにしてください
if( Request.Form["submitbutton1"] != null)
{
// Code for function 1
}
else if(Request.Form["submitButton2"] != null )
{
// code for function 2
}
がんばろう。
ボタンに名前を付けて値を指定できるはずです。その後、この名前をアクションの引数としてマップします。あるいは、2つの別々のアクションリンクまたは2つのフォームを使用してください。
あなたが書くことができます:
<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="button" value="Send" />
<input type="submit" name="button" value="Cancel" />
<% Html.EndForm(); %>
そして、ページで名前== "送信"または名前== "キャンセル"かどうかを確認してください...
私がActionSelectNameについて嫌いなことは、IsValidNameがコントローラー内のすべてのアクションメソッドに対して呼び出されることです。なぜそれがこのように機能するのか私にはわかりません。ボタンの機能に基づいてボタンごとに名前が異なるソリューションが好きですが、アクションメソッドにはフォーム内のボタンと同じ数のパラメータを指定する必要があるという事実は嫌いです。私はすべてのボタンタイプのためのenumを作成しました:
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で定義されているボタンタイプのいずれかと一致する場合は、actionパラメータの中から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" />
これが私にとって最適なものです。
<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();
}
ご使用のブラウザが入力ボタンの属性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") />
}
HTML 5の使用に制限がない場合は、formaction
属性と共に<button>
タグを使用できます。
<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
私はこの「問題」にも遭遇しましたが、name
属性を追加することによってかなり論理的な解決策を見つけました。私はこの問題を他の言語で抱えていることを思い出すことができませんでした。
http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2
次のコードの意味value
属性は、厳密に型指定されたリソースファイルや定数をチェックする特別なコードのために need なしで変更、ローカライズ、国際化が可能です。
<% 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
}
}
あなたが上記の問題を解決することができる3つの方法があります
以下は、3つのアプローチすべてを実証的な方法で要約したビデオです。
https://www.facebook.com/shivprasad.koirala/videos/vb.100002224977742/809335512483940
HTML形式: -
HTMLでは、2つのフォームを作成し、各フォームの内側に「送信」ボタンを配置する必要があります。そして、すべてのフォームのアクションは異なる/それぞれのアクションを指します。下のコードを見るとわかるように、最初のフォームが「アクション1」に投稿し、2番目のフォームがどの「送信」ボタンがクリックされたかによって「アクション2」に投稿します。
<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”コレクションには、フォームから投稿されたすべてのデータがあります。そのため、最初に「Name」値を検索し、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");
}
}
このスクリプトはdata-form-action属性を指定することを可能にします。これはすべてのブラウザでHTML5のformaction属性として機能します(邪魔にならない方法で)。
$(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()
を使うべきです。
David Findleyは、あなたがこれを行うために持っている3つの異なる選択肢を彼のASP.Netウェブログに書いています。
彼の解決策、そしてそれぞれの長所と短所を見るには、記事 同じ形式の複数のボタン を読んでください。私見は彼があなたがあなたの行動を飾る属性を利用する非常に洗練された解決策を提供します。
これは私が使うテクニックで、まだ見たことはありません。このソリューションに影響を与えるリンク(Saajid Ismailによって投稿された)は http://weblogs.asp.net/dfindley/archive/2009/05/31/asp-net-mvc-multiple-buttons-in-the-sameです-form.aspx )。それは何の問題もなくローカライズをするためにDylan Beattieの答えを適応させます。
ビューで:
<% 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;
}
}
}
これは私が複数の画像やテキストボタンを扱うために書いた拡張メソッドです。
これが画像ボタンの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
ボタンは 'value'のみを持ち、個別の 'text'属性を持ちません。私のボタンは異なる文脈の下では異なることを言っています - しかし同じ「コマンド」にマップしています。 == "Add to cart"
をコーディングする必要があるよりも、この方法で名前を抽出することをお勧めします。
私は正しい場所にコメントするのに十分な担当者を持っていませんが、私はこれに一日中費やしましたので共有したいです。
"MultipleButtonAttribute"ソリューションを実装しようとすると、ValueProvider.GetValue(keyValue)
が誤ってnull
を返すことになります。
System.Web.MVCバージョン3.0を参照していたのは4.0でした(他のアセンブリは4.0です)。なぜ私のプロジェクトが正しくアップグレードされなかったのか私にはわかりませんし、他に明白な問題もありませんでした。
それであなたのActionNameSelectorAttribute
が動かないなら...それをチェックしてください。
私はパーティーにかなり遅れています、しかしここに行きます...私の実装は@mkozickiから借りますが、間違ってしまうために必要なハードコードされた文字列が少なくて済みます。 フレームワーク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
goodnessの使用に注意してください。
[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;
}
}
これが私が見つけた最良の方法です。
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);
}
}
}
コード臭いのないマルチ送信ボタンの未来をお楽しみください。
ありがとうございました
//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();
}
私はすべての解決策を統合しようとし、フォーム上の複数のボタンを扱いやすくする[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);
}
ボタンの名前がアクションメソッドの名前と一致していることに注目してください。この記事では、値を持つボタンとインデックスを持つボタンの使い方についても説明します。
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" />
}
拡張メソッドを使った私の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')" >
Mkozickiの回答に基づいて、私は少し違う解決方法を思い付きました。まだActionNameSelectorAttribute
を使っていますが、 'Save'と 'Sync'の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の投稿に従うだろうということも指摘したい。
送信ボタンごとに以下を追加するだけです。
$('#btnSelector').click(function () {
$('form').attr('action', "/Your/Action/);
$('form').submit();
});
[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>
}
HtmlHelper 用に ActionButton メソッドを作成しました。 OnClickイベント の中で javascript のビットを持つ通常の入力ボタンを生成し、指定されたController/Actionにフォームを送信します。
あなたはそのようなヘルパーを使います
@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));
}
これらのメソッドにはさまざまなパラメータがありますが、使いやすくするために、必要なパラメータだけを取り込むようにオーバーロードを作成できます。
Ajaxフォームを使用するときは、ActionLinksをPOST HttpMethodと共に使用し、AjaxOptions.OnBeginイベントでフォームをシリアル化できます。
InsertActionとUpdateActionの2つのアクションがあるとしましょう。
<form>
@Html.Hidden("SomeField", "SomeValue")
@Ajax.ActionLink(
"Insert",
"InsertAction",
null,
new AjaxOptions {
OnBegin = "OnBegin",
UpdateTargetId = "yourDiv",
HttpMethod = "POST" })
@Ajax.ActionLink(
"Update",
"UpdateAction",
null,
new AjaxOptions {
OnBegin = "OnBegin",
UpdateTargetId = "yourDiv",
HttpMethod = "POST" })
</form>
ジャバスクリプト
function OnBegin(xhr, settings) {
settings.data = $("form").serialize();
}
カスタムヘルパー(プロジェクトのルートにあるApp_Codeフォルダー内にファイル「Helpers.cshtml」を作成します)を使用して( 'onclick'イベントで)フォームの 'action'属性を必要なものに書き換えて送信それ。
ヘルパーは次のようになります。
@helper SubmitButton(string text, string controller,string action)
{
var uh = new System.Web.Mvc.UrlHelper(Context.Request.RequestContext);
string url = @uh.Action(action, controller, null);
<input type=button onclick="(
function(e)
{
$(e).parent().attr('action', '@url'); //rewrite action url
//create a submit button to be clicked and removed, so that onsubmit is triggered
var form = document.getElementById($(e).parent().attr('id'));
var button = form.ownerDocument.createElement('input');
button.style.display = 'none';
button.type = 'submit';
form.appendChild(button).click();
form.removeChild(button);
}
)(this)" value="@text"/>
}
そしてそれを次のように使います。
@Helpers.SubmitButton("Text for 1st button","ControllerForButton1","ActionForButton1")
@Helpers.SubmitButton("Text for 2nd button","ControllerForButton2","ActionForButton2")
...
あなたのフォームの中に.