2009-12-04 14 views
7

Este comportamiento está haciendo que me pregunte acerca de mi cordura ..Si cambia el valor de modelo después de POST, la Forma sigue mostrando valor anterior

Tengo un formulario que tiene dos lugares que aceptan la entrada, llamémosles ValorA y ValorB. El usuario puede ingresar un valor en cualquiera de los dos y el formulario envía.

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

la acción del controlador se ve así:

public ActionResult MyControllerAction(MyViewModel viewModel) 
{ 

// hacer algunas otras cosas ...

return PartialView("MyPartialView", viewModel); 
} 

El modelo de vista es simplemente esto:

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

Ahora, la pieza inesperada. Supongamos que la página se carga inicialmente y ValueB tiene un valor de 7. El usuario cambia ValueA a 5 y envía el formulario. Puedo poner un punto de interrupción en la acción del controlador y ver ambos valores en el parámetro viewModel. En este punto, ValueA es 5 y ValueB es 0 (debido a la configuración de ValueA). La acción devuelve viewModel como parte de PartialView. De vuelta en el parcial, puedo poner un punto de interrupción en el Html.TextBox ("ValorB", Model.ValueB, ...) de la línea y ver que es de hecho ValorB 0. Pero cuando la forma hace al navegador, ValorB todavía tiene una valor de 7. Y aquí es donde estoy atascado. Incluso he cambiado el objetivo de actualización a un div diferente, por lo que el parcial simplemente escupe la forma en algún lugar completamente diferente, pero todavía tiene el valor original de 7, a pesar de que he visto a través de depuración que el valor de 0 a volver desde el controlador.

¿Hay algo que me falta?

+0

Me di cuenta de que hay un pequeño error en mi ejemplo, pero el problema persiste. En lugar de poner a cero el otro valor en el modelo de vista, esa lógica debe ocurrir en la acción del controlador, ya que no puedo garantizar la prioridad de configuración de propiedad durante el enlace del modelo. –

Respuesta

7

Aquí es el código de la Fuente MVC para el cuadro de texto:

 string attemptedValue = (string)htmlHelper.GetModelStateValue(name, typeof(string)); 
       tagBuilder.MergeAttribute("value", attemptedValue ?? ((useViewData) ? htmlHelper.EvalString(name) : valueParameter**), isExplicitValue); 
       break; 

y el 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; 
    } 

Así que lo que sucede es el HTML "ayudante" busca el texto valor del cuadro, comparando el nombre, en su ViewData.ModalState, si es en el diccionario ModelState, se ignora por completo el valor que nos ha facilitado.

Así que todo lo que si (valor> 0) {ValorA = 0; } no importa porque va a usar los valores publicados en ModelState si los nombres coinciden.

La manera en que yo he fijado esto es para vencer a la ModalState antes de la vista hace que para ciertos valores que desee meterse con en mis modelos de vista. Esto es código que he utilizado:

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

¡Gracias por la información jfar! En función de su respuesta, descarté que los ayudantes de cuadro de texto utilizaran entradas html. Solo había dos entradas en mi formulario que necesitaban este comportamiento, así que opté por la simplicidad antes que tener que recorrer el diccionario ModelState. Tu publicación fue muy perspicaz y me llevó a la solución, ¡así que gracias de nuevo! –

+0

¡Gracias! Esto fue una gran ayuda para mí también. – nbushnell

+0

¿Tiene alguna idea de por qué Microsoft implementó el TextBox 'HTML helper' de esta manera? – gdoron

0

gracias jfar .. este es el 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 
6

despejar todo el ModelState también puede hacer el truco:

ViewData.ModelState.Clear(); 
+0

Cuatro años después ... Esta simple línea resolvió el problema para mí. – RitchieD

0

Como se menciona en @jfar, eliminar la variable del ModelState en su controlador hará el truco. Sin embargo, puedes hacerlo con menos código (en estos días, al menos).

ModelState.Remove("ValueA"); 
Cuestiones relacionadas