45

MVC 3.0のデフォルトのモデルバインダーが非シーケンシャルインデックス(単純なモデルタイプと複雑なモデルタイプの両方)を処理できるというのは本当ですか?私はそれがすべきであることを示唆する投稿に出くわしました、しかし私のテストではそうではないようです。

与えられたポストバック値:

items[0].Id = 10
items[0].Name = "Some Item"
items[1].Id = 3
items[1].Name = "Some Item"
items[4].Id = 6
items[4].Name = "Some Item"

そしてコントローラーメソッド:

public ActionResult(IList<MyItem> items) { ... }

ロードされる値はアイテム0と1のみです。項目4は単に無視されます。

カスタムインデックス(リストへのモデルバインディング)を生成するための多くのソリューションを見てきましたが、それらはすべて以前のバージョンのMVCをターゲットにしているように見え、ほとんどは少し「ヘビーハンド」のIMOです。

私は何かが足りないのですか?

4

6 に答える 6

78

私はこれを機能させています。参照記事で説明されているように、一般的なインデックスの非表示入力を追加することを忘れないでください。

で隠された入力name = Items.Indexは重要な部分です

<input type="hidden" name="Items.Index" value="0" />
<input type="text" name="Items[0].Name" value="someValue1" />

<input type="hidden" name="Items.Index" value="1" />
<input type="text" name="Items[1].Name" value="someValue2" />

<input type="hidden" name="Items.Index" value="3" />
<input type="text" name="Items[3].Name" value="someValue3" />

<input type="hidden" name="Items.Index" value="4" />
<input type="text" name="Items[4].Name" value="someValue4" />

お役に立てれば

于 2011-12-22T01:01:24.450 に答える
5

Steve Sandersonのアプローチから派生したこのヘルパーメソッドは、はるかに単純で、コレクション内の任意のアイテムをアンカーするために使用でき、MVCモデルバインディングで機能するようです。

public static IHtmlString AnchorIndex(this HtmlHelper html)
{
    var htmlFieldPrefix = html.ViewData.TemplateInfo.HtmlFieldPrefix;
    var m = Regex.Match(htmlFieldPrefix, @"([\w]+)\[([\w]*)\]");
    if (m.Success && m.Groups.Count == 3)
        return
            MvcHtmlString.Create(
                string.Format(
                    "<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />",
                    m.Groups[1].Value, m.Groups[2].Value));
    return null;
}

たとえば、EditorTemplateで呼び出すか、次のように入力を生成する他の場所で呼び出すだけで、インデックスアンカーの隠れた変数が該当する場合は生成されます。

@model SomeViewModel
@Html.AnchorIndex()
@Html.TextBoxFor(m => m.Name)
... etc.

スティーブ・サンダーソンのアプローチに比べていくつかの利点があると思います。

  1. これは、EditorForおよび列挙可能オブジェクトを処理するための他の組み込みメカニズムと連携します。したがって、ItemsIEnumerable<T>ビューモデルのプロパティである場合、次のように機能します。

    <ul id="editorRows" class="list-unstyled"> @Html.EditorFor(m => m.Items) @* Each item will correctly anchor allowing for dynamic add/deletion via Javascript *@ </ul>

  2. それはより単純で、これ以上マジックストリングを必要としません。

  3. データ型に対して単一のEditorTemplate/DisplayTemplateを持つことができ、リスト内のアイテムで使用されない場合、それは単に何も動作しません。

唯一の欠点は、バインドされているルートモデルが列挙可能である場合(つまり、パラメーターオブジェクトグラフのより深い場所にあるプロパティではなく、Actionメソッド自体へのパラメーター)、最初の非シーケンシャルインデックスでバインドが失敗することです。残念ながら、.IndexDefaultModelBinderの機能は、ルート以外のオブジェクトに対してのみ機能します。このシナリオでは、上記のアプローチを使用する唯一のオプションが残っています。

于 2014-01-24T04:11:02.187 に答える
4

あなたが参照した記事は古いもの(MVC2)ですが、私が知る限り、これはデフォルトのmodelbinderを使用してバインドコレクションをモデル化するための事実上の方法です。

Bassamが言うように、非順次インデックスが必要な場合は、インデクサーを指定する必要があります。インデクサーは数値である必要はありません。

これには、 SteveSandersonのBeginCollectionItemHtmlHelperを使用します。インデクサーをGUIDとして自動的に生成します。これは、コレクションアイテムのHTMLが非シーケンシャルである場合に、数値インデクサーを使用するよりも優れたアプローチだと思います。

于 2011-12-22T03:57:01.290 に答える
2

今週は苦労していましたが、Bassamの答えが私を正しい軌道に乗せるための鍵でした。数量フィールドを持つことができる在庫アイテムの動的リストがあります。アイテムのリストが1からnまで変化する可能性があることを除いて、彼らが選択したアイテムの数を知る必要がありました。

私の解決策は結局かなり単純でした。2つのプロパティを持つItemVMというViewModelを作成しました。ItemIDと数量。ポストアクションでは、これらのリストを受け入れます。インデックスをオンにすると、数量がnullの場合でも、すべてのアイテムが渡されます。サーバー側で検証して処理する必要がありますが、反復を行うと、この動的リストを処理するのは簡単です。

私の見解では、私は次のようなものを使用しています:

@foreach (Item item in Items)
{
<input type="hidden" name="OrderItems.Index" value="@item.ItemID" />
<input type="hidden" name="OrderItems[@item.ItemID].ItemID" value="@item.ItemID" />
<input type="number" name="OrderItems[@item.ItemID].Quantity" />
}

これにより、0から始まるインデックスのリストが得られますが、コントローラーでの反復により、新しい強く型付けされたモデルから必要なすべてのデータが抽出されます。

public ActionResult Marketing(List<ItemVM> OrderItems)
...
        foreach (ItemVM itemVM in OrderItems)
            {
                OrderItem item = new OrderItem();
                item.ItemID = Convert.ToInt16(itemVM.ItemID);
                item.Quantity = Convert.ToInt16(itemVM.Quantity);
                if (item.Quantity > 0)
                {
                    order.Items.Add(item);
                }
            }

次に、数量が0より大きいアイテムのコレクションとアイテムIDが作成されます。

この手法は、Visual Studio2015のEF6を利用するMVC5で機能しています。これは、私のようにこのソリューションを検索する人に役立つかもしれません。

于 2015-10-10T15:04:16.820 に答える
1

または、このjavascript関数を使用してインデックスを修正します:(明らかにEntityNameとFieldNameを置き換えます)

function fixIndexing() {
        var tableRows = $('#tblMyEntities tbody tr');

        for (x = 0; x < tableRows.length; x++) {
            tableRows.eq(x).attr('data-index', x);

            tableRows.eq(x).children('td:nth-child(1)').children('input:first').attr('name', 'EntityName[' + x + "].FieldName1");

            tableRows.eq(x).children('td:nth-child(2)').children('input:first').attr('name', 'EntityName[' + x + "].FieldName2");

            tableRows.eq(x).children('td:nth-child(3)').children('input:first').attr('name', 'EntityName[' + x + "].FieldName3");
        }

        return true; //- Submit Form -
    }
于 2015-01-14T08:55:57.977 に答える
1

私はより一般的なHTMLヘルパーを作成することになりました:-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;

namespace Wallboards.Web.Helpers
{
    /// <summary>
    /// Hidden Index Html Helper
    /// </summary>
    public static class HiddenIndexHtmlHelper
    {
        /// <summary>
        /// Hiddens the index for.
        /// </summary>
        /// <typeparam name="TModel">The type of the model.</typeparam>
        /// <typeparam name="TProperty">The type of the property.</typeparam>
        /// <param name="htmlHelper">The HTML helper.</param>
        /// <param name="expression">The expression.</param>
        /// <param name="index">The Index</param>
        /// <returns>Returns Hidden Index For</returns>
        public static MvcHtmlString HiddenIndexFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, int index)
        {
            var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            var propName = metadata.PropertyName;

            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("<input type=\"hidden\" name=\"{0}.Index\" autocomplete=\"off\" value=\"{1}\" />", propName, index);

            return MvcHtmlString.Create(sb.ToString());
        }
    }
}

そして、それをRazorビューのリスト要素の各反復に含めます。-

@Html.HiddenIndexFor(m => m.ExistingWallboardMessages, i)
于 2017-03-10T09:55:26.277 に答える