ASP.NET MVC でビューとアクションを管理しやすいチャンクに分割する方法を理解するのに少し苦労しています。検索を試みましたが、まだ賢明ではありません。
この特定の側面を理解しようとするために、同じページのログイン フォームと登録フォームの例を使用して状況を理解しようとする小さなテスト プロジェクトを作成しました。これに対する私のビューモデルは以下のようになります:
public class LoginOrRegisterModel
{
public LoginModel Login { get; set; }
public RegisterModel Register { get; set; }
public LoginOrRegisterModel()
{
this.Login = new LoginModel();
this.Register = new RegisterModel();
}
}
public class LoginModel
{
[Required]
public string UserName { get; set; }
[Required]
public string Password { get; set; }
}
public class RegisterModel
{
[Required]
public string UserName { get; set; }
[Required]
public string Password { get; set; }
}
次に、メインのアクションについて考え始めました。
public ActionResult Index()
{
return View(new LoginOrRegisterModel());
}
...そして表示...
@model MvcSandbox.Models.LoginOrRegisterModel
@{
ViewBag.Title = "Index";
}
<h2>Login Or Register</h2>
@Html.Partial("Login", model: Model.Login)
@Html.Partial("Register", model: Model.Register)
...部分的なビューで...
@model MvcSandbox.Models.LoginModel
@{
ViewBag.Title = "Login";
}
<h2>Login</h2>
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
@Html.EditorFor(model => model)
<button>Login</button>
}
@model MvcSandbox.Models.RegisterModel
@{
ViewBag.Title = "Register";
}
<h2>Register</h2>
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
@Html.EditorFor(model => model)
<button>Register</button>
}
LoginOrRegisterModel のプロパティが null でないことを確認する必要があることがわかりました。それ以外の場合はエラーが発生しました: The model item passed into the dictionary is of type 'MvcSandbox.Models.LoginOrRegisterModel', but this dictionary requires model item of type 「MvcSandbox.Models.LoginModel」。
ここまでは問題ありませんが、どちらのフォームも同じフィールド名と ID を持ち、何もしないインデックス ページにポスト バックするため、現時点ではあまり役に立ちません。
HTML ソース:
<h2>Login Or Register</h2>
<h2>Login</h2>
<form action="/Membership" method="post"><div class="editor-label"><label for="UserName">UserName</label></div>
<div class="editor-field"><input class="text-box single-line" data-val="true" data-val-required="The UserName field is required." id="UserName" name="UserName" type="text" value="" /> <span class="field-validation-valid" data-valmsg-for="UserName" data-valmsg-replace="true"></span></div>
<div class="editor-label"><label for="Password">Password</label></div>
<div class="editor-field"><input class="text-box single-line" data-val="true" data-val-required="The Password field is required." id="Password" name="Password" type="text" value="" /> <span class="field-validation-valid" data-valmsg-for="Password" data-valmsg-replace="true"></span></div>
<button>Login</button>
</form>
<h2>Register</h2>
<form action="/Membership" method="post"><div class="editor-label"><label for="UserName">UserName</label></div>
<div class="editor-field"><input class="text-box single-line" data-val="true" data-val-required="The UserName field is required." id="UserName" name="UserName" type="text" value="" /> <span class="field-validation-valid" data-valmsg-for="UserName" data-valmsg-replace="true"></span></div>
<div class="editor-label"><label for="Password">Password</label></div>
<div class="editor-field"><input class="text-box single-line" data-val="true" data-val-required="The Password field is required." id="Password" name="Password" type="text" value="" /> <span class="field-validation-valid" data-valmsg-for="Password" data-valmsg-replace="true"></span></div>
<button>Register</button>
</form>
とにかく、投稿ごとにロジックを分けておきたかったので、私がやろうとしたことは、各フォーム投稿を異なるアクションにすることでした。そして、これは私がひどく間違っていると思うところです。
基本的に、検証が失敗した場合、ページをロードするときにモデルの状態を実際にバックアップして構築するために何かをする必要があると考えましたが、以下にあるものにたどり着き、どのようなアプローチをとるべきかについて迷っています代わりは。
public class MembershipController : Controller
{
//
// GET: /Membership/
public ActionResult Index()
{
if (TempData["ModelState"] != null)
ModelState.Merge((ModelStateDictionary)TempData["ModelState"]);
return View(new LoginOrRegisterModel());
}
[HttpPost]
public ActionResult Login(LoginModel model)
{
if (ModelState.IsValid)
{
// do something
}
TempData["ModelState"] = ModelState;
return RedirectToAction("Index");
}
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// do something
}
TempData["ModelState"] = ModelState;
return RedirectToAction("Index");
}
}
...景色とともに...
@model MvcSandbox.Models.LoginModel
@{
ViewBag.Title = "Login";
}
<h2>Login</h2>
@using (Html.BeginForm("Login", "Membership"))
{
@Html.ValidationSummary(true)
@Html.EditorFor(model => model)
<button>Login</button>
}
@model MvcSandbox.Models.RegisterModel
@{
ViewBag.Title = "Register";
}
<h2>Register</h2>
@using (Html.BeginForm("Register", "Membership"))
{
@Html.ValidationSummary(true)
@Html.EditorFor(model => model)
<button>Register</button>
}
問題は、各モデルに同じ名前のフィールドがあり、どちらが送信されたかに関係なく両方のフォームに検証が表示され、HtmlFieldPrefix を使用しようとしたときに検証がまったく得られないように見えるためです。
モデルの状態と検証について頭を悩ませることなく、アクションとビューを管理および保守可能なチャンクに分割する方法についてのアドバイスをいただければ幸いです。
アップデート:
以下のコードを改善するように見える部分的なアクションを使用するように、アプローチを少し変更しました。
public ActionResult Index()
{
return View();
}
public ActionResult Login()
{
if (TempData["LoginModelState"] != null)
ModelState.Merge((ModelStateDictionary)TempData["LoginModelState"]);
return View(new LoginModel());
}
[HttpPost]
public ActionResult Login(LoginModel model)
{
if (ModelState.IsValid)
{
// do something
}
TempData["LoginModelState"] = ModelState;
return RedirectToAction("Index");
}
public ActionResult Register()
{
if (TempData["RegisterModelState"] != null)
ModelState.Merge((ModelStateDictionary)TempData["RegisterModelState"]);
return View(new RegisterModel());
}
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// do something
}
TempData["RegisterModelState"] = ModelState;
return RedirectToAction("Index");
}
私のインデックスビューは次のとおりです。
@{
ViewBag.Title = "Index";
}
<h2>Login Or Register</h2>
@Html.Action("Login")
@Html.Action("Register")
ログインして登録します。
@model MvcSandbox.Models.LoginModel
<h2>Login</h2>
@using (Html.BeginForm("Login", "Membership"))
{
@Html.ValidationSummary(true)
@Html.EditorFor(model => model)
<button>Login</button>
}
@model MvcSandbox.Models.RegisterModel
<h2>Register</h2>
@using (Html.BeginForm("Register", "Membership"))
{
@Html.ValidationSummary(true)
@Html.EditorFor(model => model)
<button>Register</button>
}
今私にとって、これは以前のアプローチよりも利点があり、実際にはもう少しうまくいくように見え、LoginOrRegisterModel の非常に率直に言って恐ろしく厄介なアイデアの必要性を取り除きます。物事がより複雑になり、UI がリファクタリングされ、モデルやコード、ビューだけでなく、潜在的に多くのリファクタリングが行われるようになりました。
デフォルトのモデルバインダーを置き換えて、記述子に基づいたある種のモデルバインディングを持ち、コントローラーアクションをある種のコマンドプロセッサーとして機能させて、投稿されたパーシャルに基づいて正しいハンドラーを起動するような印象を受けます以下のMystere Manが述べているように、リダイレクトに起因する更新の問題を解決してください。