ASP.Net MVC Html.HiddenFor з неправильним значенням


132

Я використовую MVC 3 у своєму проекті, і я бачу дуже дивну поведінку.

Я намагаюся створити приховане поле для певного значення на моїй моделі, проблема полягає в тому, що з певних причин значення, встановлене в полі, не відповідає значенню в моделі.

напр

У мене є цей код, як тест:

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

Я думаю, що обидва приховані поля матимуть однакове значення. Що я роблю, це встановити значення 1 на перший раз, коли я показую View, а потім після подання я збільшую значення поля Model на 1.

Отже, перший раз, коли я рендерую сторінку, обидва елементи керування мають значення 1, але вдруге виведені значення такі:

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

Як бачите, перше значення є правильним, але друге значення здається таким же, як і в перший раз, коли я показую Вид.

Що я пропускаю? Чи є помічники * для Html певним чином кешувати значення? Якщо так, як я можу відключити кешування?

Спасибі за вашу допомогу.


Я просто випробував щось інше. Якщо я видаляю виклик HiddenFor і дозволю лише прихований дзвінок, але використовуючи ім'я "Крок", воно також надає лише перше значення (1).
willvv

1
трапляється і в
Орен,

Відповіді:


191

Це нормально, і це, як працюють помічники 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 %>" />

5
Я дуже вдячний з цього приводу в блозі Саймона Інса. Я приймаю висновок, щоб переконатися, що ваш робочий процес є правильним. Тож якщо ви прийняли дійсну модель перегляду та щось зробили з нею, то перенаправляйте на дію підтвердження, навіть якщо це також просто відновлює та відображає еквівалентну модель. Це означає, що у вас свіжий ModelState. blogs.msdn.com/b/simonince/archive/2010/05/05/… (пов'язане з публікації, про яку я писав сьогодні: oceanbites.blogspot.com/2011/02/mvc-renders-wrong-value.html )
Ліза

2
Мені дуже подобається MVC3, але цей біт справді незграбний. Я сподіваюся, що вони це виправлять у MVC4.
KennyZ

5
Ого, цей мене змусив їхати досить довго. Я в основному використовував першу пропозицію, але тільки що назвав ModelState.Clear () перед поверненням. Це, здається, працює чудово, чи є причина, щоб не використовувати Clear?
Джейсон

1
". Видалити" не працював для мене. Але ModelState.Clear () зробив безпосередньо перед поверненням в Controller. Написання на замовлення Ваших прихованих також буде добре. Це все відбувається тому, що розробники не хочуть втрачати свої "формулярні значення", якщо вони натискають "подати", а БД не зберігає правильно. Найкраще рішення: не називайте різні поля на одній сторінці однаковим іменем / ідентифікатором.
Декстер

1
FYI ця прикрою поведінкою було мило перенесено до ASP.NET Core на випадок, коли хтось хвилюється, що все покращиться
Джон Харгров

19

Я зіткнувся з тією ж проблемою, коли писав Майстра, який показує різні частини більшої моделі на кожному кроці.
Дані та / або помилки з "Крок 1" змішатимуться зі "Крок 2" тощо, поки я нарешті не зрозумів, що ModelState винен "винним".

Це було моє просте рішення:

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

return View(model[newPageIndex]);

10
ModelState.Clear()вирішив мою проблему із послідовними запитами POST у подібній ситуації.
Еван Мулавський

Дякуємо за підказку ModelState.Clear () Evan. Це була аномалія, з якою я ніколи раніше не стикався. У мене було кілька послідовних публікацій ajax.beginform, і один з них зберігав значення попереднього допису. Налагодження чорної діри. Хтось знає, чому це відбувається кешування?
Роб

1

Цей код не працюватиме

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

Я занадто бореться з тією ж ситуацією, я думаю, коли я використовую однаковий стан моделі між дзвінками, і коли я змінюю властивість моделі в бекенді. Хоча для мене це не має значення, якщо я використовую textboxfor чи hiddenfor.

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

Не впевнений, чи це допомагає, але просто врахуйте ..

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.