2009-12-04 11 views
7

Questo comportamento mi sta facendo chiedo per la mia sanità mentale ..se il valore cambia a modello dopo post, Modulo mostra ancora vecchio valore

Ho una forma che ha due posti che accettano input, chiamiamoli valoreA e valoreB. L'utente può inserire un valore in uno e il modulo viene inviato.

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

L'azione di controllo è simile al seguente:

public ActionResult MyControllerAction(MyViewModel viewModel) 
{ 

// fare altre cose ...

return PartialView("MyPartialView", viewModel); 
} 

Il ViewModel è semplicemente questo:

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

Ora, il pezzo inaspettato. Dire che la pagina inizialmente carica e ValoreB ha un valore di 7. L'utente modifica ValoreA a 5 e il modulo viene inviato. Posso inserire un punto di interruzione nell'azione del controller e vedere entrambi i valori nel parametro viewModel. A questo punto, ValoreA è 5 e ValoreB è 0 (a causa dell'impostazione di ValoreA). L'azione restituisce viewModel come parte di PartialView. Indietro nel parziale, posso mettere un punto di interruzione sulla Html.TextBox ("ValueB", Model.ValueB, ...) la linea e vedere che è davvero ValueB 0. Ma quando la forma rende al browser, ValueB ha ancora un valore di 7. E questo è dove sono bloccato. Ho anche cambiato il bersaglio Update per un div diverso, in modo che il parziale appena sputa il modulo in un posto completamente diverso, ma ha ancora il valore originale di 7, anche se ho visto attraverso il debug che il valore è stato 0 tornando dalla controller.

C'è qualcosa che mi manca?

+0

Mi sono reso conto che c'è un leggero difetto nel mio esempio, ma il problema si verifica ancora. Invece di azzerare l'altro valore nel modello di vista, tale logica deve avvenire nell'azione del controllore, poiché non posso garantire la precedenza delle impostazioni della proprietà durante l'associazione del modello. –

risposta

7

ecco il codice dalla Fonte MVC per la casella di testo:

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

e al codice per 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; 
    } 

Quindi, ciò che accade è l'Html "Helper" cerca il testo valore della casella, abbinando il nome, nella tua ViewData.ModalState, se nel dizionario ModelState, è ignora completamente il valore che hai fornito.

Quindi tutto che se (valore> 0) {ValueA = 0; } non ha importanza perché utilizzerà i valori pubblicati in ModelState se i nomi corrispondono.

Il modo in cui ho riparato questo è quello di soffiare via la ModalState prima che la vista rende per certi valori che voglio pasticciare con i miei modelli di vista. Questo è un codice che ho usato:

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

Grazie per l'intuizione jfar! In base alla tua risposta, ho bloccato gli helper della casella di testo utilizzando invece gli input html. C'erano solo due input sul mio modulo che avevano bisogno di questo comportamento, quindi sono andato per semplicità a dover passare attraverso il dizionario ModelState. Il tuo post è stato molto perspicace e mi ha portato alla soluzione, quindi grazie ancora! –

+0

Grazie! Questo è stato di grande aiuto anche per me. – nbushnell

+0

Hai idea del motivo per cui Microsoft ha implementato il TextBox "HTML helper" in questo modo? – gdoron

0

grazie jfar .. questo è il codice 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

Rimozione degli inceppamenti dal ModelState potrebbe anche fare il trucco:

ViewData.ModelState.Clear(); 
+0

Quattro anni dopo .... Questa semplice linea risolveva il problema per me. – RitchieD

0

Come @jfar menzionato, rimuovere la variabile dal ModelState nel controller farà il trucco. Puoi farlo con meno codice, anche se (almeno in questi giorni).

ModelState.Remove("ValueA"); 
Problemi correlati