モデルバインダー&隠しフィールド
-
07-09-2020 - |
質問
私はこの質問をするのに便利な単純化されたテストシナリオを持っています:製品は多くのコンポーネントを持つことができ、コンポーネントは多くの製品に属することができます。EFはクラスを生成しました、私は次のようにそれらをスリム化しました:
public partial class Product
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Component> Components { get; set; }
}
public partial class Component
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
コンポーネントの作成は、次のコントローラーアクションを介して実行されます:
public ActionResult Create(int ProductId)
{
Product p = db.Products.Find(ProductId);
Component c = new Component();
c.Products.Add(p);
return PartialView(c);
}
[HttpPost]
public ActionResult Create(Component model)
{
db.Components.Add(model);
db.SaveChanges();
}
GETメソッドによって返されるビューは次のようになります:
@model Test.Models.Product
<fieldset>
<legend>Product</legend>
<div class="display-label">Name</div>
<div class="display-field">@Model.Name</div>
</fieldset>
@Html.Action("Create", "Component", new {ProductId = Model.Id})
<p>
@Html.ActionLink("Edit", "Edit", new { id=Model.Id }) |
@Html.ActionLink("Back to List", "Index")
</p>
これから、コンポーネントの作成は上記を介して同じページで処理されることがわかります Html.Action
-そのビューのコードは次のとおりです:
@model Test.Models.Component
@using Test.Models
<script type="text/javascript">
function Success() {
alert('ok');
}
function Failure() {
alert('err');
}
</script>
@using (Ajax.BeginForm("Create", "Component", new AjaxOptions
{
HttpMethod = "Post",
OnSuccess = "Success",
OnFailure = "Failure"
}))
{
<fieldset>
<legend>Components</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
@Html.HiddenFor(x => x.Products.First().Id)
@Html.HiddenFor(x => x.Products)
@foreach (Product p in Model.Products)
{
@Html.Hidden("Products[0].Id", p.Id)
}
@foreach (Product p in Model.Products)
{
@Html.Hidden("[0].Id", p.Id)
}
</fieldset>
<input type="submit" value="go" />
}
わかっただからこれは私が苦労しているものです:私は必要です model
[HttpPost]のパラメータが正しく入力されるように戻ります。null製品で新しいコンポーネントを作成することはできないため、製品が含まれている必要があります。製品を取得するには、製品のidを介して検索する必要があります。私は私ができるはずだと期待しています:
model.Products.Add(db.Products.Find(model.Products.First().Id));
またはいくつかのそのようなもの、に依存しています model
idを受信します。これは、ビューがおそらく隠されたフィールドにidを配置する必要があることを意味し、ビューコードからわかるように、これを入力するためにいくつかの試みを行いましたが、これらはすべて失敗しました。
通常、私は正しい命名法を生成する責任があるので、*Forメソッドを好みます。もし。製品は単数形でした(。製品)、私はそれを参照することができます x => x.Product.Id
そして、すべてがうまくいくでしょうが、それは複数形なので、私はできません x => x.Products.Id
だから私は試した x => x.Products.First().Id
正しい値をコンパイルして生成しますが、名前を取得します Id
(モデルバインダーがそれを考えているので、これは間違っています Component.Id
そしてない Component.Products[0].Id
.
私の第二の試みは、できるようにすることでした HiddenFor
反復する(私がしたいように EditorFor
):
@Html.HiddenFor(x => x.Products)
しかし、それは何も生成しません-私はこのヘルパーが反復しないことを読みました。私は試しました x => x.Products.First()
しかし、それはコンパイルさえしません。最後に、私は*Forを放棄し、自分で名前をコード化することにしました:
@foreach (Product p in Model.Products)
{
@Html.Hidden("Products[0].Id", p.Id)
それは正しいように見えますが、ポストバックには私の値が表示されません(Products.Count
== 0).私はいくつかの投稿でその形式が次のようになるはずであることを見ました [0].Id
しかし、それもうまくいきません。grr...
私はこれを次のようにコード化することができます:
@Html.Hidden("ProductId", p.Id)
そして、次のようにコントローラーアクションを再宣言します:
[HttpPost] ActionResult Create(Component model, int ProductId)
しかし、それはeeckyのようです。これがとても難しいと信じるのは難しいです。誰でも助けることができますか?
- e
p.s.誰かが気にするなら、私はダウンロードできるプロジェクトを持っています
解決
それらを書く代わりに foreach
loopsエディタテンプレートを使ってみてください:
<fieldset>
<legend>Components</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
@Html.EditorFor(x => x.Products)
</fieldset>
と対応するエディタテンプレート内(~/Views/Shared/EditorTemplates/Product.cshtml
)
@model Product
@Html.HiddenFor(x => x.Id)