Se o valor muda no modelo após o post, Form ainda exibe valor antigo
-
13-09-2019 - |
Pergunta
Este comportamento está me fazendo perguntar sobre a minha sanidade mental ..
Eu tenho um formulário que tem dois lugares que aceitam entrada, vamos chamá-los de valorA e valorB. O usuário pode inserir um valor em qualquer um e os submete o formulário.
<div id="MyUpdateTarget">
<% using (Ajax.BeginForm("MyControllerAction", new AjaxOptions { UpdateTargetId = "MyUpdateTarget" })) { %>
<%=Html.TextBox("ValueA", Model.ValueA, new Dictionary<string, object> {
{ "onchange", "$('#SubmitButton').click(); return false;" },
}) %>
<%=Html.TextBox("ValueB", Model.ValueB, new Dictionary<string, object> {
{ "onchange", "$('#SubmitButton').click(); return false;" },
}) %>
<input id="SubmitButton" type="submit" value="Save" style="display: none;" />
<% } %>
</div>
a ação do controlador se parece com isso:
public ActionResult MyControllerAction(MyViewModel viewModel)
{
// fazer algumas outras coisas ...
return PartialView("MyPartialView", viewModel);
}
O ViewModel é simplesmente esta:
public class MyViewModel
{
private int _valueA;
private int _valueB;
public int ValueA
{
get
{
return _valueA;
}
set
{
if (value > 0)
{
ValueB = 0;
}
_valueA = value;
}
}
public int ValueB
{
get
{
return _valueB;
}
set
{
if (value > 0)
{
ValueA = 0;
}
_valueB = value;
}
}
}
Agora, a peça inesperado. Dizem que a página inicialmente cargas e valorB tem um valor de 7. O usuário muda valorA a 5 e os submete o formulário. Eu posso colocar um ponto de interrupção na ação do controlador e ver ambos os valores no parâmetro viewmodel. Neste ponto, é valueA 5 e valorB é 0 (devido à definição de valueA). A ação retorna o ViewModel como parte do PartialView. Back in the parcial, posso colocar um ponto de interrupção na Html.TextBox ( "valorB", Model.ValueB, ...) de linha e ver que valorB é realmente 0. Mas quando o formulário torna para o navegador, valorB ainda tem um valor de 7. E isso é onde eu estou preso. Eu até mudaram o alvo Atualização de uma div diferente, de modo que as justas espetos lateral para a algum lugar forma completamente diferente, mas ainda tem o valor original de 7, mesmo que eu vi através de depuração que o valor é 0 voltando do controlador.
Existe algo que eu estou ausente?
Solução
Aqui está o código da Fonte MVC para a caixa de texto:
string attemptedValue = (string)htmlHelper.GetModelStateValue(name, typeof(string));
tagBuilder.MergeAttribute("value", attemptedValue ?? ((useViewData) ? htmlHelper.EvalString(name) : valueParameter**), isExplicitValue);
break;
E o código para GetModelStateValue ()
internal object GetModelStateValue(string key, Type destinationType) {
ModelState modelState;
if (ViewData.ModelState.TryGetValue(key, out modelState)) {
if (modelState.Value != null) {
return modelState.Value.ConvertTo(destinationType, null /* culture */);
}
}
return null;
}
Então, o que acontece é que o HTML "Helper" olha para o valor da caixa de texto, combinando o nome, em sua ViewData.ModalState, se o seu no dicionário ModelState, ele ignora completamente o valor que fornecido.
Assim, tudo o que se (valor> 0) {valorA = 0; } Não importa, porque a sua vai usar os valores lançados no ModelState se os nomes correspondem.
A maneira que eu fixo isso é para soprar a ModalState antes da vista torna para certos valores que eu quero mexer com em meus modelos de exibição. Isto é algum código que eu usei:
public static void SanitizeWildcards( Controller controller, params string[] filterStrings )
{
foreach( var filterString in filterStrings )
{
var modelState = controller.ModelState;
ModelState modelStateValue;
if( modelState.TryGetValue(filterString,out
controller.ModelState.SetModelValue(filterString, new ValueProviderResult("","", null));
}
}
Outras dicas
Limpeza de todo o ModelState também pode fazer o truque:
ViewData.ModelState.Clear();
graças jfar .. este é o código vb:
Sub CleanForm(ByVal ParamArray Fields() As String)
Dim modelStateValue As ModelState = Nothing
For Each Field In Fields
If ModelState.TryGetValue(Field, modelStateValue) Then
ModelState.SetModelValue(Field, New ValueProviderResult(Nothing, Nothing, Nothing))
End If
Next
End Sub
Como @jfar mencionado, removendo a variável do ModelState em seu controlador irá fazer o truque. Você pode fazer isso com menos código, embora (nos dias de hoje, pelo menos).
ModelState.Remove("ValueA");