wake-up-neo.com

@ Html.HiddenFor funktioniert nicht bei Listen in ASP.NET MVC

Ich verwende ein Modell, das eine Liste als Eigenschaft enthält. Ich fülle diese Liste mit Elementen, die ich aus SQL Server hole. Ich möchte, dass die Liste in der Ansicht ausgeblendet und an die Aktion POST= weitergeleitet wird. Später möchte ich dieser Liste möglicherweise weitere Elemente mit jQuery hinzufügen, wodurch ein Array später für die Erweiterung ungeeignet wird. Normalerweise würden Sie verwenden

@Html.HiddenFor(model => model.MyList)

um diese Funktionalität zu erreichen, aber aus irgendeinem Grund ist die Liste in POST immer null.

Sehr einfache Frage, weiß jemand, warum MVC sich so verhält?

87
Anton Smith

Ich bin gerade auf dieses Problem gestoßen und habe es einfach folgendermaßen gelöst:

@for(int i = 0; i < Model.ToGroups.Length; i++)
{
    @Html.HiddenFor(model => Model.ToGroups[i])
}

Durch die Verwendung von for anstelle von foreach wird die Modellbindung korrekt ausgeführt und alle versteckten Werte in der Liste werden übernommen. Scheint der einfachste Weg zu sein, um dieses Problem zu lösen.

137
Daniel Mackay

HiddenFor ist kein DisplayFor oder EditorFor. Es funktioniert nicht mit Sammlungen, nur mit Einzelwerten.

Sie können den im MVC Futures-Projekt verfügbaren HTML-Helper "Serialize" verwenden, um ein Objekt in ein ausgeblendetes Feld zu serialisieren, oder Sie müssen den Code selbst schreiben. Eine bessere Lösung besteht darin, einfach eine ID zu serialisieren und die Daten beim Postback aus der Datenbank abzurufen.

28

Es ist ein bisschen hacken, aber wenn @Html.EditorFor Oder @Html.DisplayFor Für Ihre Liste funktionieren, wenn Sie sicherstellen möchten, dass es auf der Post-Anfrage gesendet, aber nicht sichtbar ist, können Sie es einfach mit Stil versehen display: none;, Um es stattdessen auszublenden, zB:

<div style="display: none;">@Html.EditorFor(model => model.MyList)</div>
13
Mark Rhodes

Was ist mit Newtonsoft, um das Objekt in eine JSON-Zeichenfolge zu deserialisieren, und fügen Sie diese dann in Ihr ausgeblendetes Feld ein, z. (Model.DataResponse.Entity.Commission ist eine Liste von einfachen "CommissionRange" Objekten als Sie werden in der JSON sehen)

@using (Ajax.BeginForm("Settings", "AffiliateProgram", Model.DataResponse, new AjaxOptions { UpdateTargetId = "result" }))
   {
      string commissionJson = JsonConvert.SerializeObject(Model.DataResponse.Entity.Commission);
      @Html.HiddenFor(data => data.DataResponse.Entity.Guid)
      @Html.Hidden("DataResponse_Entity_Commission", commissionJson)
      [Rest of my form]
   }

Rendert als:

<input id="DataResponse_Entity_Commission" name="DataResponse_Entity_Commission" type="hidden" value="[{"RangeStart":0,"RangeEnd":0,"CommissionPercent":2.00000},{"RangeStart":1,"RangeEnd":2,"CommissionPercent":3.00000},{"RangeStart":2,"RangeEnd":0,"CommissionPercent":2.00000},{"RangeStart":3,"RangeEnd":2,"CommissionPercent":1.00000},{"RangeStart":15,"RangeEnd":10,"CommissionPercent":5.00000}]">

In meinem Fall mache ich ein paar JS-Sachen, um den JSON-Code im ausgeblendeten Feld zu bearbeiten, bevor ich ihn zurückschicke

In meinem Controller verwende ich dann Newtonsoft erneut, um Folgendes zu deserialisieren:

string jsonCommissionRange = Request.Form["DataResponse_Entity_Commission"];
List<CommissionRange> commissionRange = JsonConvert.DeserializeObject<List<CommissionRange>>(jsonCommissionRange);
8
Adam Hey

Html.HiddenFor ist nur für einen Wert vorgesehen. Sie müssen Ihre Liste auf irgendeine Weise serialisieren, bevor Sie das ausgeblendete Feld erstellen.

Wenn Ihre Liste beispielsweise vom Typ "Zeichenfolge" ist, können Sie die Liste in eine durch Kommas getrennte Liste einfügen und die Liste nach dem Posten wieder in Ihren Controller aufteilen.

6
Kyle Trauberman

Ich habe gerade herausgefunden (nachdem ich ein paar Stunden lang versucht hatte herauszufinden, warum die Modellwerte nicht auf den Controller zurückgingen), dass versteckte Werte dem EditorFor folgen sollten.

Wenn ich nichts anderes falsch mache, habe ich das gefunden. Ich werde den Fehler nicht noch einmal machen.

Im Kontext eines Modells, das eine Liste einer anderen Klasse enthält.

Das wird NICHT funktionieren:

        @{
            for (int i = 0; i < Model.Categories.Count; i++)
            {
                <tr>
                    <td>
                        @Html.HiddenFor(modelItem => Model.Categories[i].Id)
                        @Html.HiddenFor(modelItem => Model.Categories[i].ProductCategoryId)
                        @Html.HiddenFor(modelItem => Model.Categories[i].CategoryName)                            
                        @Html.DisplayFor(modelItem => Model.Categories[i].CategoryName)                            
                    </td>
                    <td>
                        @Html.HiddenFor(modelItem => Model.Categories[i].DailyPurchaseLimit)                                                        
                        @Html.EditorFor(modelItem => Model.Categories[i].DailyPurchaseLimit)
                        @Html.ValidationMessageFor(modelItem => Model.Categories[i].DailyPurchaseLimit)
                    </td>
                    <td style="text-align: center">
                        @Html.HiddenFor(modelItem => Model.Categories[i].IsSelected)                            
                        @Html.EditorFor(modelItem => Model.Categories[i].IsSelected)
                    </td>
                </tr>
            }
        }

Wo wird das denn sein ......

            for (int i = 0; i < Model.Categories.Count; i++)
            {
                <tr>
                    <td>
                        @Html.HiddenFor(modelItem => Model.Categories[i].Id)
                        @Html.HiddenFor(modelItem => Model.Categories[i].ProductCategoryId)
                        @Html.HiddenFor(modelItem => Model.Categories[i].CategoryName)                            
                        @Html.DisplayFor(modelItem => Model.Categories[i].CategoryName)                            
                    </td>
                    <td>
                        @Html.EditorFor(modelItem => Model.Categories[i].DailyPurchaseLimit)
                        @Html.HiddenFor(modelItem => Model.Categories[i].DailyPurchaseLimit)                            
                        @Html.ValidationMessageFor(modelItem => Model.Categories[i].DailyPurchaseLimit)
                    </td>
                    <td style="text-align: center">
                        @Html.EditorFor(modelItem => Model.Categories[i].IsSelected)
                        @Html.HiddenFor(modelItem => Model.Categories[i].IsSelected)                            
                    </td>
                </tr>
            }
4
AntDC

Sie können einen Blick darauf werfen Lösung .

Fügen Sie nur HiddenFor in das EditorTemplate ein.

Und in deiner Ansicht schreibe dies: @Html.EditorFor(model => model.MyList)

Es sollte funktionieren.

3

Ich habe angefangen, den Quellcode für HiddenFor zu durchforsten, und ich denke, die Roadblockierung, die Sie sehen, ist, dass Ihr komplexes Objekt MyList implizit nicht in den Typ string konvertierbar ist Das Framework behandelt Ihren Model -Wert als null und macht das value -Attribut leer.

3
Cᴏʀʏ

Konfrontiert das gleiche Problem. Ohne for-Schleife wird nur das erste Element der Liste gepostet. Nach dem Durchlaufen der for-Schleife kann die vollständige Liste beibehalten und erfolgreich gepostet werden.

 @if (Model.MyList!= null)
    {
    for (int i = 0; i < Model.MyList.Count; i++)
      {
        @Html.HiddenFor(x => x.MyList[i])
      }
    }
3
Keerthi

Eine andere Option wäre:

<input type="hidden" [email protected](string.Join(",", Model.MyList)) />
2
TiagoBrenck

Eine andere Möglichkeit, dies zu beheben, besteht darin, jedem Objekt in Ihrer Liste eine ID zuzuweisen, dann @Html.DropDownListFor(model => model.IDs) zu verwenden und ein Array auszufüllen, das die IDs enthält.

1
deckeresq

vielleicht spät, aber ich habe eine Erweiterungsmethode für ausgeblendete Felder aus der Sammlung erstellt (mit einfachen Datentypelementen):

Hier ist es also:

/// <summary>
/// Returns an HTML hidden input element for each item in the object's property (collection) that is represented by the specified expression.
/// </summary>
public static IHtmlString HiddenForCollection<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression) where TProperty : ICollection
{
    var model = html.ViewData.Model;
    var property = model != null
                ? expression.Compile().Invoke(model)
                : default(TProperty);

    var result = new StringBuilder();
    if (property != null && property.Count > 0)
    {
        for(int i = 0; i < property.Count; i++)
        {
            var modelExp = expression.Parameters.First();
            var propertyExp = expression.Body;
            var itemExp = Expression.ArrayIndex(propertyExp, Expression.Constant(i));

            var itemExpression = Expression.Lambda<Func<TModel, object>>(itemExp, modelExp);

            result.AppendLine(html.HiddenFor(itemExpression).ToString());
        }
    }

    return new MvcHtmlString(result.ToString());
}

Die Verwendung ist so einfach wie:

@Html.HiddenForCollection(m => m.MyList)
1
Gh61

Die foreach -Schleife anstelle einer for -Schleife ist möglicherweise eine etwas sauberere Lösung.

@foreach(var item in Model.ToGroups)
{
    @Html.HiddenFor(model => item)
}
0
Sebastian