134 votes

ASP.Net MVC Html.HiddenFor avec une valeur incorrecte

Je suis en utilisant MVC 3 dans mon projet, et je vois un comportement très étrange.

Je suis en train de créer un champ caché pour une valeur particulière sur mon Modèle, le problème est que, pour une raison quelconque, la valeur définie sur le terrain ne correspond pas à la valeur dans le Modèle.

par exemple

J'ai ce code, tout comme un test:

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

Je pense que les deux champs cachés aurait la même valeur. Ce que j'ai à faire est de définir la valeur à 1, la première fois que je l'affichage, puis après la présentation je l'augmentation de la valeur du Modèle de champ de 1.

Donc, la première fois que je le rendu de la page deux contrôles ont la valeur 1, mais la deuxième fois, les valeurs de rendu sont celles-ci:

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

Comme vous pouvez le voir, la première valeur est correcte, mais la deuxième valeur qui semble être la même que la première fois que je l'affichage.

Ce qui me manque? Sont l' *Pour Html helpers la mise en cache les valeurs d'une certaine façon? Si oui, comment puis-je désactiver la mise en cache?.

Merci pour votre aide.

195voto

Darin Dimitrov Points 528142

C'est normal et c'est comment HTML helpers de travail. Leur première utilisation de la valeur de la requête POST et après que la valeur dans le modèle. Cela signifie que même si vous modifiez la valeur du modèle de votre contrôleur de l'action si il y a la même variable dans la requête POST votre modification sera ignoré et le Posté valeur sera utilisée.

Une solution possible est de supprimer cette valeur à partir du modèle de l'état dans l'action du controller qui est de la tentative de modifier la valeur:

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

Une autre possibilité est d'écrire une assistance HTML personnalisée qui vous permettra de toujours utiliser la valeur du modèle et d'ignorer les valeurs POST.

Et encore une autre possibilité:

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

20voto

Peter B Points 69

J'ai rencontré le même problème lors de l'écriture d'un assistant qui montre différentes parties d'un modèle plus grand à chaque étape.
Les données et / ou les erreurs de "Step 1" seraient mélangées avec "Step 2", etc., jusqu'à ce que je réalise enfin que ModelState était à blâmer.

C'était ma solution simple:

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

1voto

Ce code ne fonctionnera pas

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

...parce que HiddenFor toujours (!) lit à partir de ModelState pas le modèle lui-même. Et s'il ne trouve pas le "Étape" clé, il va produire de la valeur par défaut pour ce type de variable qui sera de 0 dans ce cas

Voici la solution. Je l'ai écrit pour moi, mais ne me dérange pas de le partager parce que je vois beaucoup de gens sont aux prises avec cette coquine HiddenFor helper.

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

Ensuite, vous avez juste l'utiliser comme d'habitude à l'intérieur de vous afficher:

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

Il vaut la peine de parler, il fonctionne avec des collections de trop.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X