ASP.Net MVC Html.HiddenFor с неправильным значением - PullRequest
125 голосов
/ 17 января 2011

Я использую MVC 3 в своем проекте и вижу очень странное поведение.

Я пытаюсь создать скрытое поле для определенного значения в моей модели, проблема в том, что по какой-то причине значение, установленное в поле, не соответствует значению в модели.

, например

У меня есть этот код, просто как тест:

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

Я думаю, что оба скрытых поля будут иметь одинаковое значение. Что я делаю, так это устанавливаю значение 1 при первом отображении вида, а затем после отправки увеличиваю значение поля модели на 1.

Итак, при первом отображении страницы оба элемента управления имеют значение 1, но при этом отображаются следующие значения:

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

Как видите, первое значение верное, но второе значение похоже на то, которое я отображаю в первый раз.

Что мне не хватает? Помогают ли * Html-помощники кэшировать значения каким-либо образом? Если так, как я могу отключить это кэширование?.

Спасибо за вашу помощь.

Ответы [ 4 ]

182 голосов
/ 17 января 2011

Это нормально, и так работают HTML-помощники. Сначала они используют значение запроса POST, а затем значение в модели. Это означает, что даже если вы измените значение модели в действии вашего контроллера, если в запросе POST есть такая же переменная, ваша модификация будет проигнорирована, и будет использовано значение POSTed.

Один из возможных обходных путей - удалить это значение из состояния модели в действии контроллера, которое пытается изменить значение:

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

Другая возможность - написать собственный HTML-помощник, который всегда будет использовать значение модели и игнорировать значения POST.

И еще одна возможность:

<input type="hidden" name="Step" value="<%: Model.Step %>" />
16 голосов
/ 16 августа 2013

Я столкнулся с той же проблемой при написании мастера, который показывает разные части большей модели на каждом шаге.
Данные и / или ошибки из «шага 1» смешивались с «шагом 2» и т. Д., ПокаНаконец я понял, что виноват ModelState.

Это было мое простое решение:

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

return View(model[newPageIndex]);
1 голос
/ 10 октября 2014

Этот код не будет работать

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

... потому что HiddenFor всегда (!) Читает из ModelState, а не саму модель. И если он не находит ключ «Step», он выдаст значение по умолчанию для этого типа переменной, которое в этом случае будет равно 0

Вот решение. Я написал это для себя, но не против поделиться им, потому что я вижу, что многие люди борются с этим непослушным помощником 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)
            };
        }
    }
}

Тогда вы просто используете его, как обычно, из своего представления:

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

Стоит отметить, что он работает и с коллекциями.

0 голосов
/ 06 апреля 2018

Я слишком борюсь с той же ситуацией, я думаю, когда я использую одно и то же состояние модели между вызовами и когда я изменяю свойство модели в бэкэнде.Хотя для меня это не имеет значения, если я использую textboxfor или hiddenfor.

Я просто обхожу ситуацию, используя скрипты страниц для хранения значения модели в виде переменной js, потому что для этого мне нужно скрытое поле вначало.

Не уверен, поможет ли это, но просто подумайте ...

...