wake-up-neo.net

ASP.Net MVC Html.HiddenFor mit falschem Wert

Ich verwende MVC 3 in meinem Projekt und sehe ein sehr seltsames Verhalten.

Ich versuche, ein verstecktes Feld für einen bestimmten Wert in meinem Modell zu erstellen. Das Problem besteht darin, dass der im Feld gesetzte Wert aus irgendeinem Grund nicht dem Wert im Modell entspricht.

z.B.

Ich habe diesen Code nur als Test:

<%:Html.Hidden("Step2", Model.Step) %>
<%:Html.HiddenFor(m => m.Step) %>

Ich würde denken, dass beide ausgeblendeten Felder den gleichen Wert haben würden. Was ich mache, ist, den Wert auf 1 zu setzen, wenn ich die Ansicht zum ersten Mal wiedergebe, und dann nach der Übermittlung den Wert des Felds Modell um 1 erhöhen.

Beim ersten Rendern der Seite haben beide Steuerelemente den Wert 1, beim zweiten Mal werden jedoch folgende Werte angezeigt:

<input id="Step2" name="Step2" type="hidden" value="2" />
<input id="Step" name="Step" type="hidden" value="1" />

Wie Sie sehen, ist der erste Wert korrekt, aber der zweite Wert scheint derselbe zu sein als beim ersten Anzeigen der Ansicht.

Was vermisse ich? Werden die Werte von * For Html auf irgendeine Weise zwischengespeichert? Wenn ja, wie kann ich diese Zwischenspeicherung deaktivieren?.

Danke für Ihre Hilfe.

123
willvv

Das ist normal und so funktionieren HTML-Helfer. Sie verwenden zuerst den Wert der Anforderung POST und danach den Wert im Modell. Dies bedeutet, dass Ihre Änderung ignoriert wird, auch wenn Sie den Wert des Modells in Ihrer Controller-Aktion ändern, wenn dieselbe Variable in der Anforderung von POST vorhanden ist, und der POST-Wert wird verwendet.

Eine mögliche Problemumgehung besteht darin, diesen Wert aus dem Modellstatus in der Controller-Aktion zu entfernen, die versucht, den Wert zu ändern:

// remove the Step variable from the model state 
// if you want the changes in the model to be
// taken into account
ModelState.Remove("Step");
model.Step = 2;

Eine andere Möglichkeit besteht darin, einen benutzerdefinierten HTML-Helper zu schreiben, der immer den Wert des Modells verwendet und POST -Werte ignoriert.

Und noch eine andere Möglichkeit:

<input type="hidden" name="Step" value="<%: Model.Step %>" />
180
Darin Dimitrov

Beim Schreiben eines Wizard, der bei jedem Schritt verschiedene Teile eines größeren Modells zeigt, ist das gleiche Problem aufgetreten.
Daten und/oder Fehler aus "Schritt 1" würden mit "Schritt 2" usw. verwechselt werden, bis ich schließlich merkte, dass ModelState "schuld" war.

Dies war meine einfache Lösung:

if (oldPageIndex != newPageIndex)
{
    ModelState.Clear(); // <-- solution
}

return View(model[newPageIndex]);
15
Peter B

Dieser Code funktioniert nicht 

// remove the Step variable from the model state
// if you want the changes in the model to be
// taken into account
ModelState.Remove("Step");
model.Step = 2;

... weil HiddenFor immer (!) von ModelState nicht das Modell selbst liest. Wenn die "Step" -Taste nicht gefunden wird, wird der Standardwert für diesen Variablentyp erstellt, der in diesem Fall 0 ist

Hier ist die Lösung. Ich habe es für mich selbst geschrieben, aber es macht nichts aus, es zu teilen, weil ich sehe, dass viele Leute mit diesem frechen HiddenFor-Helfer kämpfen.

public static class CustomExtensions
{
    public static MvcHtmlString HiddenFor2<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        ReplacePropertyState(htmlHelper, expression);
        return htmlHelper.HiddenFor(expression);
    }

    public static MvcHtmlString HiddenFor2<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
    {
        ReplacePropertyState(htmlHelper, expression);
        return htmlHelper.HiddenFor(expression, htmlAttributes);
    }

    public static MvcHtmlString HiddenFor2<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
    {
        ReplacePropertyState(htmlHelper, expression);
        return htmlHelper.HiddenFor(expression, htmlAttributes);
    }

    private static void ReplacePropertyState<TModel, TProperty>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        string text = ExpressionHelper.GetExpressionText(expression);
        string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(text);
        ModelStateDictionary modelState = htmlHelper.ViewContext.ViewData.ModelState;
        ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);

        if (modelState.ContainsKey(fullName))
        {                
            ValueProviderResult currentValue = modelState[fullName].Value;
            modelState[fullName].Value = new ValueProviderResult(metadata.Model, Convert.ToString(metadata.Model), currentValue.Culture);
        }
        else
        {
            modelState[fullName] = new ModelState
            {
                Value = new ValueProviderResult(metadata.Model, Convert.ToString(metadata.Model), CultureInfo.CurrentUICulture)
            };
        }
    }
}

Dann verwenden Sie es wie gewohnt aus Ihrer Sicht:

@Html.HiddenFor2(m => m.Id)

Erwähnenswert ist, dass es auch mit Sammlungen funktioniert.

1

Ich kämpfe zu sehr mit der gleichen Situation, in der ich denke, dass ich zwischen den Aufrufen denselben Modellstatus verwende und wenn ich eine Modelleigenschaft im Backend ändere. Dabei ist es mir egal, ob ich textboxfor oder hidden für verwende.

Ich umgehe die Situation einfach, indem ich Seitenskripts verwende, um den Modellwert als eine js-Variable zu speichern, da ich am Anfang das Hiddenfield zu diesem Zweck brauche.

Nicht sicher, ob das hilft, aber nur überlegen.