これは主にこの問題のコメントのフォローアップですが、コメントするのに十分な評判がありません...
ASP.Net MVCはラベル値をコントローラーにポストバックします
単純なモデルがあるとしましょう:
public class SimpleClass
{
public String Label { get; set; }
public String FirstName { get; set; }
}
ラベルはユーザー/クライアントに基づいて変更されるため、DataAttributeにすることはできません。ポストバック処理の問題が発生した場合は、ページ全体を再描画する必要があります。これが前の投稿の問題の核心です。受け入れられている解決策は、これを行うことです。
@Html.DisplayTextFor(model => model.Label)
@Html.HiddenFor(model => model.Label)
@Html.EditorFor(model => model.FirstName)
それが機能するという点でそれは理にかなっています。しかし、私たちのモデルははるかに複雑で広範囲です。この方法では、非常に汚い解決策のように見える大量の非表示フィールドが生成されます。
これは私にJPのコメントをもたらします:
ASP.Net MVCはラベル値をコントローラーにポストバックします
解決策は、モデルをリロードすることです。ただし、これは単なるリロードではなく、クライアント側のデータ変更を保持する必要があるため、マージでもあります。
default: SimpleClass { Label="TheLabel", FirstName="Rob"}
postedback: SimpleClass { Label="", FirstName="Steve" }
we want: SimpleClass { Label="TheLabel", "FirstName="Steve" }
私の質問は、MVCには、正しくマージされるようにポストバックされたフィールドを知る良い方法があるのでしょうか?空白のプロパティではなく、ポストバックフィールドのみをマージする必要があります。
それとも、ポストバック全体を単にajax化して、フォームを送信しない方がよいでしょうか?これにより、送信時のすべてのモデルの再読み込みの問題が回避されます。
パブロの功績を認めるために、私は彼の解決策を受け入れました。彼の解決策の私の簡単な例を見るには、以下の回答のRobertHarveyのコメントを確認してください。
ここでの主な問題は、WebFormsのPostBackの概念をMVCに適合させようとすることです。物事が自動的に状態を保持するステートフルポストバックのようなものはありません。
ビューにバインドされているViewModelと、ビューによってコントローラーに投稿されているViewModelのみがあります。それらは必ずしも同じタイプである必要はありません。つまり、コントローラーは、ユーザーが実際に変更できるデータのみを受信する必要があります。初期のViewModelの一部であるが、読み取り専用である多くのプロパティを持つ大きなオブジェクトは受信しないでください。
ラベルは通常、読み取り専用のテキストを表し、編集可能なフォーム要素ではありません。そのため、非表示フィールドを使用する必要があるのはそのためです。
はい、場合によっては、コントローラーに元のデータを再読み込みし、投稿した新しいデータと同期する必要があることを意味しますが、これは必ずしも悪いことではありません。ユーザーが手動で編集できないビューに読み取り専用データをバインドする場合、後で投稿に返されるデータを実際に信頼するべきではありません。あなたのhtmlがそれを読み取り専用にしようとするかもしれないからといって、私があなたの知らないうちに投稿を操作して最終的にあなたの「読み取り専用」データを変更できないという意味ではありません。
あなたが言及した2番目の質問を読みましたが、その外観から、彼の主な問題は、同じViewModelを再利用しようとしたため、すべてのデータが欠落し、モデルが無効であったことでした。これに対する解決策は確かに非常に単純で、必要なものだけを新しいViewModelタイプとして投稿し、残りはコントローラーに任せます。
[OPから移動]
これは、パブロが疑問に思っている人たちに提案していることだと思います。この問題を解決するのに良いパターンのようです。
モデル:
public class SimpleClass : SimpleClassPostBack
{
public String Label { get; set; }
public SimpleClass()
{
// simulate default loading
Label = "My Label";
FirstName = "Rob";
}
}
// contains only editable by the user fields
public class SimpleClassPostBack
{
public String FirstName { get; set; }
}
コントローラのアクション:
[HttpGet]
public ActionResult SimpleClassExample3()
{
SimpleClass simpleClass = new SimpleClass();
return View(simpleClass);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SimpleClassExample3(SimpleClassPostBack postBackSimpleClass)
{
Boolean errorOccurred = true;
if (!errorOccurred)
{
// do whatever success action is necessary
}
// redraw the page, an error occurred
// reload the original model
SimpleClass simpleClass = new SimpleClass();
// move the posted back data into the model
// can use fancy reflection to automate this
simpleClass.FirstName = postBackSimpleClass.FirstName;
// bind the view
return View(simpleClass);
}
見る:
@model SimpleClass
@{
ViewBag.Title = "Simple Class Example3";
}
<h2>Simple Class Example3</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<label for="FirstName">@Html.DisplayFor(m => m.Label)</label>
@Html.EditorFor(m => m.FirstName)
<br/>
<button>Submit</button>
}
クライアントからサーバーに送信する必要があるのは、サーバーがそれ自体で「把握」できないデータのみです。ユーザーが最初にそのビューに移動したときにサーバーがラベルを認識している場合、ユーザーがラベルを変更できない場合、サーバーはビューを再ロードするときにラベルが何であるかを知ることができます。
非表示フィールドを使用して、データベースオブジェクトを識別します。したがって、SimpleClass
には、非表示の入力で使用するId
のようなものが含まれているはずです。 EditorFor
にはFirstName
を使用します。フォームが投稿されたら、送信されたId
を使用してデータベースから正しいSimpleClass
を見つけ、投稿された値でそのFirstName
プロパティを変更します。 Label
プロパティはnull
になります。これは、保存する必要がないので問題ありません。投稿に問題があり、同じビューを以前と同じように送り返したい場合は、ユーザーが初めてビューにアクセスしたときと同じ方法でLabel
を再入力する必要があります。 Id
プロパティとFirstName
プロパティの値は、モデルの状態とともにビューに自動的に返送されます。
要約すれば: