2011-01-17 11 views
119

Używam MVC 3 w moim projekcie i widzę bardzo dziwne zachowanie.ASP.Net MVC Html.HiddenFor z nieprawidłową wartością

Próbuję utworzyć ukryte pole dla określonej wartości na moim modelu, problem polega na tym, że z jakiegoś powodu wartość ustawiona w polu nie odpowiada wartości w modelu.

np.

Mam ten kod, tylko jako test:

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

Myślę, że oba pola ukryte miałyby tę samą wartość. Co mam zrobić, ustaw wartość 1 przy pierwszym wyświetleniu widoku, a następnie po przesłaniu zwiększam wartość pola Model o 1.

Po raz pierwszy renderuję stronę obie kontrolki mają wartość 1, ale po raz drugi wartości świadczone są takie:

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

jak widać, pierwsza wartość jest poprawna, ale druga wartość zdaje się być taki sam jak pierwszy raz wyświetlić widok .

Czego mi brakuje? Czy pomocnicy HTML pomagają w buforowaniu wartości w jakiś sposób? Jeśli tak, jak mogę wyłączyć to buforowanie?

Dzięki za pomoc.

+0

Właśnie testowałem coś innego. Jeśli usunę połączenie HiddenFor i zezwolę tylko na połączenie ukryte, ale używając nazwy "Step", renderuje ona tylko pierwszą wartość (1). – willvv

+1

dzieje się również uzyskać –

Odpowiedz

167

To normalne i tak działają pomocnicy HTML. Najpierw używają wartości żądania POST, a następnie wartości w modelu. Oznacza to, że nawet jeśli zmodyfikujesz wartość modelu w działaniu kontrolera, jeśli w żądaniu POST jest ta sama zmienna, modyfikacja zostanie zignorowana i użyta zostanie wartość POSTed.

Możliwym rozwiązaniem jest usunięcie tej wartości ze stanu modelu w akcji kontrolera, który próbuje zmodyfikować wartość:

// 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; 

Inną możliwością jest napisać pomocnika niestandardowych HTML, który zawsze użyje wartości modelu i zignorować wartości POST.

I jeszcze inna możliwość:

<input type="hidden" name="Step" value="<%: Model.Step %>" /> 
+0

Wielkie wyjaśnienie, dzięki. Zrobiłem trzecią opcję, chociaż pierwsza wydaje się interesująca. – willvv

+5

Bardzo doceniam wpis na blogu Simona Ince'a na ten temat. Wniosek, który z tego wyciągam, to zapewnienie prawidłowego przepływu pracy. Jeśli więc zaakceptowałeś prawidłowy model widoku i zrobiłeś z nim coś, a następnie przekierowujesz do akcji potwierdzającej, nawet jeśli to po prostu powtarza i wyświetla równoważny model. Oznacza to, że masz świeże ModelState. http://blogs.msdn.com/b/simonince/archive/2010/05/05/asp-net-mvc-s-html-helpers-render-the-wrong-value.aspx (powiązane z post, który napisałem na ten temat dzisiaj: http://oceanbites.blogspot.com/2011/02/mvc-renders-wrong-value.html) – Lisa

+0

To teraz ma sens, ale dlaczego w EditorForModel będą szukać odpowiedzi wartości ponad wartości modelu? – ehdv

13

I napotkał ten sam problem pisząc kreatora, który pokazuje różne części większego modelu na każdym kroku.
Dane i/lub błędy z "Kroku 1" zostałyby pomieszane z "Kroku 2" itp., Dopóki w końcu nie zdałem sobie sprawy, że ModelState ma "winić".

To był mój proste rozwiązanie:

if (oldPageIndex != newPageIndex) ModelState.Clear(); // <-- solution 
return View(model[newPageIndex]); 
+8

'ModelState.Clear()' rozwiązał mój problem z sekwencyjnymi żądaniami POST w podobnej sytuacji. –

2

Ten kod nie zadziała

// 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; 

... bo HiddenFor zawsze odczytuje z ModelState nie samego modelu (!). A jeśli nie znajdzie klucza "Step", wygeneruje wartość domyślną dla tego typu zmiennej, która będzie wynosić 0 w tym przypadku

Oto rozwiązanie.Napisałem to dla siebie, ale nie mam nic przeciwko dzieleniu się nim, ponieważ widzę, jak wiele osób boryka się z tym niegrzecznym pomocnikiem HiddenFor.

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) 
      }; 
     } 
    } 
} 

Wtedy po prostu używać go jak zwykle od wewnątrz obejrzeć:

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

warto wspomnieć, że działa ze zbiorów zbyt.

+0

to rozwiązanie nie w pełni działa. Po następnej po wycofaniu właściwość ma wartość null w działaniu – user576510

+0

Cóż, to jest kod z produkcji, w którym działa dobrze.Nie wiem, dlaczego to nie działa, ale jeśli widzisz ukryte pole z poprawną wartością renderowaną na stronie, nie widzę żadnego oczywistego powodu, dla którego nie zostałby przywrócony we właściwości modelu. Jeśli zauważysz niewłaściwą wartość ukrytego pola na stronie - to jest inna historia, bardzo chciałbym wiedzieć, w jakich okolicznościach tak się dzieje, zanim to samo stanie się w mojej produkcji :-) Dziękuję. –