2013-08-18 17 views
26

Non riesco a capire come inviare l'intero ViewModel al controller alla funzione 'Convalida e salva'.MVC 4 ViewModel non viene rinviato al controller

Ecco il mio controller:

[HttpPost] 
public ActionResult Send(BitcoinTransactionViewModel transaction) 
{ 
} 

Qui è la forma nella vista:

<li class="check"> 
    <h3>Transaction Id</h3> 
    <p>@Html.DisplayFor(m => m.Transaction.TransactionId)</p> 
</li> 
<li class="money"> 
    <h3>Deposited Amount</h3> 
    <p>@Model.Transaction.Amount.ToString() BTC</p> 
</li> 
<li class="time"> 
    <h3>Time</h3> 
    <p>@Model.Transaction.Time.ToString()</p> 
</li> 


@using (Html.BeginForm("Send", "DepositDetails", FormMethod.Post, new { transaction = Model })) 
{ 

@Html.HiddenFor(m => m.Token); 
@Html.HiddenFor(m => m.Transaction.TransactionId); 

    @Html.TextBoxFor(m => m.WalletAddress, new { placeholder = "Wallet Address", maxlength = "34" }) 
    <input type="submit" value="Send" />  

    @Html.ValidationMessage("walletAddress", new { @class = "validation" }) 
} 

quando clicco su presentare, il conroller contiene il valore corretto del campo WalletAddress ma transaction.Transaction.Time, transaction.Transaction.Location, transaction.Transaction.TransactionId sono vuoti.

È possibile inoltrare l'intero modello al controller?

Edit:

Quando Neppure non ricevo il walletAddress nel controller. Tutto viene annullato! Quando rimuovo questa linea da sola: @Html.HiddenFor(m => m.Transaction.TransactionId); funziona e ottengo la proprietà Token sul controller, ma quando la aggiungo, tutte le proprietà dell'oggetto transaction sul controller sono NULL.

Ecco la BitcoinTransactionViewModel:

public class BitcoinTransactionViewModel 
    { 
     public string Token { get; set; } 
     public string WalletAddress { get; set; } 
     public BitcoinTransaction Transaction { get; set; } 
    } 

public class BitcoinTransaction 
    { 
     public int Id { get; set; } 
     public BitcoinTransactionStatusTypes Status { get; set; } 
     public int TransactionId { get; set; } 
     public decimal Amount { get; set; } 
     public DateTime Time { get; set; } 
     public string Location { get; set; } 
    } 

Tutte le idee?

EDIT: ho capito, la sua nella risposta segnata in basso ...

risposta

32

OK, ho lavorato tutto da capo su qualcos'altro e bumpend lo stesso problema. Solo questa volta ho capito come farlo funzionare!

Ecco la risposta per tutti coloro che potrebbero essere interessati:

A quanto pare, c'è una convenzione di denominazione. Fate attenzione:

Questo non funziona:

// Controller 
[HttpPost] 
public ActionResult Send(BitcoinTransactionViewModel transaction) 
{ 
} 

// View 
@using (Html.BeginForm("Send", "DepositDetails", FormMethod.Post, new { transaction = Model })) 
{ 

@Html.HiddenFor(m => m.Token); 
@Html.HiddenFor(m => m.Transaction.TransactionId); 
. 
. 

Questo funziona:

// Controller 
[HttpPost] 
public ActionResult Send(BitcoinTransactionViewModel **RedeemTransaction**) 
{ 
} 

// View 
@using (Html.BeginForm("Send", "DepositDetails", FormMethod.Post, new { **RedeemTransaction** = Model })) 
{ 

@Html.HiddenFor(m => m.Token); 
@Html.HiddenFor(m => m.Transaction.TransactionId); 
. 
. 

In altre parole - un errore di convenzione di denominazione! C'era un'ambiguità di denominazione tra la proprietà Model.Transaction e il mio campo transaction campo + parametro del controller. Unvelievable.

Se si verificano gli stessi problemi, assicurarsi che il nome del parametro del controller sia univoco: provare a rinominarlo in MyTestParameter o qualcosa di simile ...

Inoltre, se si desidera inviare i valori del modulo al controller, è necessario includerli come campi nascosti e si è pronti per andare.

+1

Mi piace questa risposta e ha senso, ma sfortunatamente non ha funzionato per me. Sembra che io sia bloccato in un vicolo cieco dove non importa quello che provo Non riesco a far tornare il sub-oggetto nel controller. Una cosa così frustrante. –

+0

Ho avuto lo stesso problema. Ho avuto un ViewModel denominato FileViewModel e il mio parametro di azione è stato chiamato "file". L'ho cambiato in "_file" e ha funzionato per me. –

+0

Life saver, grazie. – Chris

11

Questo non è specifico MVC. Il modulo HTML pubblica solo i valori contenuti negli elementi del modulo all'interno del modulo. Il tuo esempio non è né all'interno del modulo né in un elemento del modulo (come gli input nascosti). Devi farlo poiché MVC non si basa su View State. Mettere i campi nascosti all'interno forma:

@Html.HiddenFor(x => x.Transaction.Time) 
// etc... 

Chiedetevi però .. se l'utente non sta aggiornando questi valori .. non il metodo di azione li richiedono?

+2

Grazie per la risposta. Ho un problema diverso ora: quando creo un campo nascosto per una proprietà nel primo livello (x => x.Token) funziona, ma quando lo faccio per l'oggetto interno (X => x.Transaction.InnerField) questo non funziona! all'improvviso tutte le proprietà dell'oggetto transazione nel controller sono nulle! Hai un'idea del perché questo accada? –

+1

Richiede loro perché quando la validazione fallisce, voglio rendere lo stesso modulo con errori di validazione e non selezionare l'oggetto dal database (ma usare quello che ho già) –

+0

Devi mostrare il codice aggiornato per rispondere al nuovo errore .. –

9

Il binding del modello idrata il modello di vista nell'azione del controller tramite i valori del modulo registrati. Non vedo alcun controllo dei moduli per le variabili sopra menzionate, quindi nulla potrebbe essere posticipato. Puoi vedere se hai qualche gioia con questo?

@using (Html.BeginForm("Send", "DepositDetails", FormMethod.Post, new { transaction = Model })) 
{ 
    @Html.TextBoxFor(m => m.WalletAddress, new { placeholder = "Wallet Address", maxlength = "34" }) 
    @Html.Hidden("Time", Model.Transaction.Time) 
    @Html.Hidden("Location", Model.Transaction.Location) 
    @Html.Hidden("TransactionId", Model.Transaction.TransactionId) 
    <input type="submit" value="Send" />  

    @Html.ValidationMessage("walletAddress", new { @class = "validation" }) 
} 
+0

Quando eseguo il rendering di un campo nascosto per una proprietà interiore, improvvisamente tutte le proprietà nel controller ricevente vengono annullate! –

+0

Aggiornato, puoi dargli un altro giro? –

+0

Questo non crea perché la transazione non è una matrice. È solo un oggetto, per favore vedi nella domanda aggiornata. –

22

La firma del metodo Send che la forma sta inviando a ha un parametro denominato transazione, che sembra confondere il modello legante. Cambiare il nome del parametro di essere qualcosa che non corrisponde il nome di una proprietà del modello:

[HttpPost] 
public ActionResult Send(BitcoinTransactionViewModel model) 
{ 
} 

Inoltre, rimuovere il parametro htmlAttributes dalla vostra chiamata BeginForm, dal momento che non sta facendo nulla di utile. Diventa:

@using (Html.BeginForm("Send", "DepositDetails", FormMethod.Post)) 

Tutti i dati di ritorno dal cliente avrebbe potuto essere manomesso, così si dovrebbe inviare solo indietro l'ID univoco della transazione e quindi recuperare ulteriori informazioni su di esso dalla fonte di dati per eseguire ulteriori in lavorazione.Dovrai anche verificare qui che l'utente che ha inserito i dati abbia accesso all'ID della transazione specificata in quanto potrebbe essere stato manomesso.

+1

La mia domanda è come pubblicare l'intero oggetto, non solo il suo ID univoco. Questo perché NON voglio eseguire una selezione sul mio database né gestire la cache di dati sul lato server. –

+0

Per pubblicare l'intero oggetto è necessario eseguire ciò che gli altri hanno selezionato e creare input nascosti per ogni proprietà. Rinominare il parametro del metodo Send risolverà il problema con l'intero modello che è nullo. –

+0

Word of warning NON è sensibile al maiuscolo/minuscolo, – Stonedecroze

0

Mettere tutti i campi all'interno del modulo

@using (Html.BeginForm("Send", "DepositDetails", FormMethod.Post)) 

e assicurarsi che il modello

BitcoinTransactionViewModel 

inclusa nella vista o non?

0

Riesci a combinare solo quei 2 modelli che hai? Ecco come faccio con un modello per visualizzazione ... 1. Uso i modelli di visualizzazione per visualizzare in modo da poter passare l'intero modello e lasciare i dati crittografati. 2. Imposta la tua vista principale in questo modo ..

@model IEnumerable<LecExamRes.Models.SelectionModel.GroupModel> 
<div id="container"> 
<div class="selectLabel">Select a Location:</div><br /> 
@foreach (var item in Model) 
{   
    @Html.DisplayFor(model=>item) 
} 
</div> 

3. Creare una cartella DisplayTemplates in condivisione. Crea una vista, chiamandola come il tuo modello che vuoi passare perché un DisplayFor cerca il modello di visualizzazione che prende il nome dal modello che usi, io chiamo il mio GroupModel. Pensa a un modello di visualizzazione come a un'istanza dell'oggetto della tua enumerazione. Groupmodel Assomiglia a questo, sto semplicemente assegnando un gruppo a un pulsante.

@model LecExamRes.Models.SelectionModel.GroupModel 
@using LecExamRes.Helpers 
@using (Html.BeginForm("Index", "Home", null, FormMethod.Post)) 
{ 
<div class="mlink"> 
    @Html.AntiForgeryToken() 
    @Html.EncryptedHiddenFor(model => model.GroupKey) 
    @Html.EncryptedHiddenFor(model => model.GroupName) 
    <p> 
     <input type="submit" name="gbtn" class="groovybutton" value=" @Model.GroupKey   "> 
    </p> 
</div> 
}  

4. Ecco il controller. * GET & POST *

public ActionResult Index() 
    { 
     // Create a new Patron object upon user's first visit to the page. 
     _patron = new Patron((WindowsIdentity)User.Identity); 
     Session["patron"] = _patron;    
     var lstGroups = new List<SelectionModel.GroupModel>(); 
     var rMgr = new DataStoreManager.ResourceManager(); 
     // GetResourceGroups will return an empty list if no resource groups where found. 
     var resGroups = rMgr.GetResourceGroups(); 
     // Add the available resource groups to list. 
     foreach (var resource in resGroups) 
     { 
      var group = new SelectionModel.GroupModel(); 
      rMgr.GetResourcesByGroup(resource.Key); 
      group.GroupName = resource.Value; 
      group.GroupKey = resource.Key; 
      lstGroups.Add(group); 
     } 
     return View(lstGroups); 
    } 

    [ValidateAntiForgeryToken] 
    [HttpPost] 
    public ActionResult Index(SelectionModel.GroupModel item) 
    { 
     if (!ModelState.IsValid) 
      return View(); 

     if (item.GroupKey != null && item.GroupName != null) 
     {    
      var rModel = new SelectionModel.ReserveModel 
      { 
       LocationKey = item.GroupKey, 
       Location = item.GroupName 
      }; 

      Session["rModel"] = rModel; 
     }   
//So now my date model will have Group info in session ready to use 
     return RedirectToAction("Date", "Home"); 
    } 

5. Ora, se ho un sacco di opinioni con diversi modelli, Io di solito uso un modello legato alla vista e poi un obj sessione che prende i dati da ogni modello quindi alla fine ho i dati da inviare.

1

Provare le raccolte modulo e ottenere il valore come. Penso che questo possa funzionare.

public ActionResult Send(FormCollection frm) 
{ 
    var time = frm['Transaction.Time']; 
} 
3

Prova a ciclo con la dichiarazione folowing non con foreach

<table> 
    @for (var i = 0; i < Model.itemlist.Count; i++) 
    { 
     <tr> 
      <td> 
       @Html.HiddenFor(x => x.itemlist[i].Id) 
       @Html.HiddenFor(x => x.itemlist[i].Name) 
       @Html.DisplayFor(x => x.itemlist[i].Name) 
      </td> 
     </tr> 
    } 
</table> 
0

Il nome dell'azione ai quali i dati saranno pubblicati dovrebbe essere uguale al nome dell'azione da cui i dati vengono inviati. L'unica differenza dovrebbe essere che la seconda azione in cui i dati vengono pubblicati dovrebbe avere [HttpPost] e il metodo di pubblicazione dovrebbe servire solo a ricevere richieste.