ASP.Net MVC Html.HiddenFor с неправильным значением
Я использую 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 ответа
Это нормально, и так работают 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 %>" />
Я столкнулся с той же проблемой при написании мастера, который показывает различные части более крупной модели на каждом этапе.
Данные и / или ошибки из "шага 1" смешивались с "шагом 2" и т. Д., Пока я, наконец, не понял, что виноват ModelState.
Это было мое простое решение:
if (oldPageIndex != newPageIndex)
{
ModelState.Clear(); // <-- solution
}
return View(model[newPageIndex]);
Этот код не будет работать
// 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)
Стоит отметить, что он работает и с коллекциями.
Я слишком борюсь с той же ситуацией, я думаю, где я использую одно и то же состояние модели между вызовами и когда я изменяю свойство модели в бэкенде. Впрочем, для меня не имеет значения, использую ли я textbox для или hiddenfor.
Я просто обхожу ситуацию, используя скрипты страниц для хранения значения модели в виде переменной js, потому что для этого вначале мне нужно скрытое поле для этой цели.
Не уверен, что это поможет, но просто подумайте..