2012-11-30 9 views
7

Ho un problema che in realtà non capisco. Ho un modello molto semplice che ha un elenco come membro pubblico. Ogni volta che il mio controller rimuove un elemento dal modello sul postback, gli helper HTML TextBoxFor() non sembrano raccogliere le modifiche. Sembra che questi aiutanti nascondano qualcosa, ma non riesco a puntare il dito su di esso.MVC4 TextBox HTML Per non funzionare dopo la modifica di ViewModel

una demo/Repro può essere trovato qui: http://broken.azurewebsites.net

Repro

  1. Spostarsi http://broken.azurewebsites.net
  2. Nota i valori 4 colonne popolati con zero valori basati
  3. premere il pulsante "test" POST indietro la pagina in cui rimuovo il primo elemento nell'elenco
  4. Avviso I valori "reali" sono corretti e l'elemento 0 ha è stato rimosso, tuttavia il problema qui è con i valori visualizzati tramite TextBoxFor(). Non riesco a capire perché restituisce 0 quando quell'elemento non esiste più.

modelle

public class ItemViewModel 
{ 
    public string Description { get; set; } 
    public decimal? Amount { get; set; } 
} 

public class TestViewModel 
{ 
    public TestViewModel() 
    { 
     Items = new List<ItemViewModel>(); 
    } 

    public List<ItemViewModel> Items { get; set; } 
} 

controller

public class HomeController : Controller 
{ 
    public ActionResult Index() 
    { 
     var model = new TestViewModel(); 

     for (var i = 0; i < 4; i++) 
     { 
      model.Items.Add(new ItemViewModel { Description = i.ToString(), Amount = i }); 
     } 

     return View(model); 
    } 

    [HttpPost] 
    public ActionResult Index(TestViewModel model) 
    { 
     model.Items.RemoveAt(0); 

     return View(model); 
    } 

} 

Visualizza

@model Demo.Models.TestViewModel 
@using (Html.BeginForm()) 
{ 
    <table> 
     <thead> 
      <tr><td>Description</td><td>Amount</td><td>Real-Description</td><td>Real-Amount</td></tr> 
     </thead> 
     <tbody> 
      @for (var i = 0; i < Model.Items.Count; i++) 
      { 
       var ii = i; 
       <tr> 
        <td>@Html.TextBoxFor(m => m.Items[ii].Description)</td> 
        <td>@Html.TextBoxFor(m => m.Items[ii].Amount)</td> 
        <td>@Model.Items[ii].Description</td> 
        <td>@Model.Items[ii].Amount</td> 
       </tr> 
      } 
     </tbody> 
    </table> 
    <button>Test</button> 
} 

risposta

15

Modifica metodo l'indice posto di seguito per il comportamento desiderato:

[HttpPost] 
    public ActionResult Index(TestViewModel model) 
    { 
     ModelState.Clear();  

     model.Items.RemoveAt(0); 

     return View(model); 
    } 

Ecco perché:

La TextBoxFor si lega al valore posto nella ModelState invece di valore del modello. Quindi, nella tua applicazione, quando premi il pulsante Test, le caselle di testo sono legate al valore che hanno già e non vengono aggiornate sul postback anche dopo la modifica dei valori del modello. Ad esempio, se le caselle di testo nella prima riga mostrano 0 e rimarranno vincolate a quel valore anche dopo il postback e il modello sottostante sta avendo valore 1. La ragione di questo comportamento è la convalida. Se si prevede un valore int nella casella di testo e gli input utente "SomeTextValue", il modello legatore non sarà in grado di collegarsi alla proprietà int e si troverà in uno stato di errore di convalida. Quindi si vorrebbe che l'utente visualizzi un errore che dice "SomeTextValue" non è un numero intero. Inserisci un numero intero perché ti aspetteresti che il valore inserito dall'utente sia presente.

Rick Strahl spiega perfettamente in questo post del blog

http://www.west-wind.com/weblog/posts/2012/Apr/20/ASPNET-MVC-Postbacks-and-HtmlHelper-Controls-ignoring-Model-Changes

E Marca Wilson di seguito:

http://forums.asp.net/post/3688022.aspx

+0

Grazie, io sono sicuro che questo è il problema. Verificherò e riferirò. –

Problemi correlati