MVC3 Razorを使用しています。ビューには2つの送信ボタンが設定されていますが、問題は、両方の送信ボタンがモデルの検証を引き起こすことです。検証のために、特定の入力コントロールを使用して個々の送信ボタンを接続します。
私はこれが数ヶ月前であることを知っていますが、ここでの解決策は不必要に複雑であるように見え、受け入れられた答えはまだありません。入力に同じ名前を付け、異なる値を指定する場合、変数として入力の名前を持つ文字列を含めるだけで、コントローラーにその値を取得できます。これが私がこの問題を解決した方法です:
見る:
<input type="submit" id="EnterprisePush" name="btnSubmit" value="Push" />
<input type="submit" id="EnterprisePull" name="btnSubmit" value="Pull" />
コントローラ:
[HttpPost]
public ActionResult EnterpriseAdmin(int id, string btnSubmit, FormCollection collection)
{
switch (btnSubmit) {
case "Push":
/* Do Something here */
break;
case "Pull":
/* Do Something else here */
break;
}
ブラウザは、どの送信ボタンを押しても、常にフォーム全体を送信します。
最良の解決策は、name
属性の値が同じで、value
属性の値が異なる2つの送信ボタンを使用することです。
フォームを送信すると、ボタンの値も送信されます。そのフォーム送信を処理するアクションで、ボタンの値を確認し、それに基づいて正しい検証を実行します。
フォームには次のようなものがあります。
<button type="submit" name="Command" value="command1">Do Command #1</button>
<button type="submit" name="Command" value="command2">Do Command #2</button>
フォームモデルは次のようになります。
public class MyFormModel() {
public string Command {get;set;}
public string SomeOtherVal {get;set;}
}
Controller\actionは次のようになります。
public ActionResult HandleFormSubmit(MyFormModel model) {
if (model.Command == "command1") {
// do something
} else if (model.Command == "command2") {
// do something else
}
}
まず、CSSクラス「キャンセル」を追加するだけで、キャンセルボタンでクライアント検証を無効にできます。参照: MVC 3の[キャンセル]送信ボタンでクライアント側の検証を無効にする
第二に、前述のように送信要素のフォーム名をテストするだけでなく、カスタムアクションセレクターを使用できます。これが私のコメントです。私はもともとコメントに示されているブログ投稿からそれを取りました。
/// <summary>
/// Used to vary an action method based on which button in a form was pressed. This
/// is useful but is an anti-pattern because it couples the controller to names
/// used in the form elements.
/// </summary>
/// <remarks>
/// See the example at http://weblogs.asp.net/dfindley/archive/2009/05/31/asp-net-mvc-multiple-buttons-in-the-same-form.aspx
/// </remarks>
public class AcceptButtonAttribute : ActionMethodSelectorAttribute
{
public string ButtonName { get; set; }
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
var req = controllerContext.RequestContext.HttpContext.Request;
return !string.IsNullOrEmpty(req.Form[this.ButtonName]);
}
}
コントローラーで:
[HttpPost]
[ActionName("Edit")]
[AcceptButton(ButtonName = "Cancel")]
public ActionResult Edit_Cancel(MyModel model)
{
return RedirectToAction("Index");
}
[HttpPost]
[AcceptButton(ButtonName = "Save")]
public ActionResult Edit(MyModel model)
{
// do real work here
}
[ActionName( "Edit")]属性は、MVCに別のメソッド名を使用しているが、それが編集アクション用であることを伝えるために必要であることに注意してください。
そしてあなたの見解では:
<input type="submit" name="Save" value="Save" />
<input type="submit" name="Cancel" value="Cancel" class="cancel" />
私の解決策は2つのことをすることでした。 [保存]ボタンと別の[何かを追加]ボタンがあるとします。ユーザーが[保存]をクリックすると、クライアントの検証とサーバーの検証が実行されます。後のボタンでは、検証を行わないでください。
<input type = "submit" name = "submit-button" value = "Save" />
<input type = "submit" name = "submit-button" value = "何かを追加" onclick = "document.forms [0] .noValidate = true; document.forms [0] .submit();" />
JavaScriptが無効になっている場合、クライアントの検証は行われません。
フォーム内の送信ボタンをクリックしたときにブライアンが言っているのと同様に、フォーム全体とクリックされた送信ボタンの値が投稿されます。クリックされたボタンは、投稿された名前で区別できます。上記の例では、ユーザーが[保存]ボタンをクリックし、コントローラーのポストアクションでRequest.Form ["submit-button"]を読み取ると、「保存」が取得されます。ユーザーが[何かを追加]をクリックすると、[何かを追加]が表示されます。これがHTMLの動作方法です。
今、魔法の文字列をあちこちに持って行くために、私は通常、コントローラ内に次のようにパブリック静的クラスを持っています:
public class HomeController
{
public static class Buttons
{
public const string Save = "Save";
public const string AddSomething = "Add something";
}
// Action methods
}
したがって、フォームのレンダリングにこれらを使用できます。
<input type="submit" name="submit-button" value="@HomeController.Buttons.Save" />
また、コントローラーでクリックされたボタンを簡単に読み取ることができます。
[HttpPost]
public ActionResult Index(Model viewModel)
{
var buttonClicked = Request.Form["submit-button"];
switch (buttonClicked) {
case HomeController.Buttons.Save:
return Save(viewModel);
case HomeController.Buttons.AddSomething:
return AddSOmething(viewModel);
}
return View();
}
Saveメソッドでは、最初にModelState.IsValidであるかどうかを確認し、そうでない場合はAddSomethingメソッドでエラーをクリアします。
public ActionResult AddSomething(Model viewModel)
{
ModelState.Clear();
// your code to add something to model
return View(viewModel);
}
これは、すべてをきれいに整頓し、テストしやすくすることでした。また、submit-button html name属性の定数を導入できます。 T4MVCでもすべての定数を実行できる可能性があります。 「自動ポストバック」コンボボックスが必要な場合にも同様のソリューションが適用されますが、select要素のonchangeイベントを介して設定される非表示フィールドが必要です。
お役に立てれば。
このコードをテンプレートとして使用するだけです:
@{
var nextButtonVal = "Next >>";
var backButtonVal = "<< Back";
if (IsPost) {
if(Request["navigate"].Equals(backButtonVal)){Response.Redirect("~/pageFoo");}
if(Request["navigate"].Equals(nextButtonVal)){Response.Redirect("~/pagebar");}
}
}
<input type="submit" value="@backButtonVal" title="Back" name="navigate"/>
<input type="submit" value="@nextButtonVal" title="Next" name="navigate"/>
最後に、インテリジェント文字列を使用する代わりに、enum
を使用して各入力タグの値を決定します。かみそり構文の使用:
@Enum.GetName(typeof(YourEnumType), yourEnum.WhateverValue)
次に、コントローラーで:
public ActionResult DoSomethingBasedOnEnumValue(string enumValue)
{
YourEnumType localVar = (YourEnumType)Enum.Parse(typeof(YourEnumType), enumValue);
switch(localVar)
{
case YourEnumType.Action1:
//do something
break;
case YourEnumType.Action2:
//do something else
break;
}
return View();
}
この状況から検証モデルプロパティに[リモート]属性を使用する場合、送信ボタン名はサーバー側に送信されません。
削除するための個別のアクションが必要な場合は、これを試してください。
コントローラーに削除アクションを追加し、HttpDelete
としてマークします。
[HttpDelete]
public ActionResult Edit(int id, string foo) {
...
}
ビューでは、ボタン名はX-HTTP-Method-Override
および値はDELETE
でなければなりません
<button name="X-HTTP-Method-Override" value="DELETE" formnovalidate="formnovalidate" class="cancel">Delete</button>
注:ほとんどすべてのブラウザーは、HEAD、PUTなどの他のHTTPメソッドを許可していません、またはDELETE。ただし、HTTPリクエストにヘッダーを追加することで、X-HTTP-Method-Override
、これはサービスによって解釈され、実際に使用されるHTTPメソッドに関係なく実行されることになっています。したがって、上記のコードは、リクエストに X-HTTP-Method-Override: DELETE
。 .net frameworkは残りの処理を行い、アクションを削除するよう指示します。