web-dev-qa-db-fra.com

MVC 4 ViewModel n'est pas renvoyé au contrôleur

Je n'arrive pas à comprendre comment renvoyer le ViewModel entier au contrôleur vers la fonction 'Valider et enregistrer'.

Voici mon contrôleur:

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

Voici le formulaire dans la vue:

<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" })
}

Lorsque je clique sur soumettre, le contrôleur contient la valeur correcte du champ walletAddress mais transaction.Transaction.Time, transaction.Transaction.Location, transaction.Transaction.TransactionId Sont vides.

Existe-t-il un moyen de renvoyer le modèle entier au contrôleur?

Modifier:

Quand je ne reçois même pas le walletAddress dans le contrôleur. Tout est annulé! Lorsque je supprime cette ligne seule: @Html.HiddenFor(m => m.Transaction.TransactionId); cela fonctionne et j'obtiens la propriété Token sur le contrôleur, mais quand je l'ajoute, toutes les propriétés de l'objet transaction sur le contrôleur sont NULL .

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

Des idées?

EDIT: je l'ai compris, c'est dans la réponse marquée ci-dessous ...

28
Uri Abramson

OK, j'ai travaillé sur autre chose et je me suis de nouveau heurté au même problème. Mais cette fois, j'ai compris comment le faire fonctionner!

Voici la réponse pour tous ceux qui pourraient être intéressés:

Apparemment, il existe une convention de dénomination. Faites attention:

Cela ne fonctionne pas:

// 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);
.
.

Cela fonctionne:

// 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);
.
.

En d'autres termes - une erreur de convention de dénomination! Il y avait une ambiguïté de nom entre le Model.Transaction propriété et mon transaction champ de formulaire + paramètre de contrôleur. Incroyable.

Si vous rencontrez les mêmes problèmes, assurez-vous que le nom du paramètre de votre contrôleur est unique - essayez de le renommer en MyTestParameter ou quelque chose comme ça ...

De plus, si vous souhaitez envoyer des valeurs de formulaire au contrôleur, vous devrez les inclure en tant que champs masqués, et vous êtes prêt à partir.

35
Uri Abramson

La signature de la méthode Send que le formulaire publie a un paramètre nommé transaction, ce qui semble confondre le classeur de modèle. Modifiez le nom du paramètre pour qu'il ne corresponde pas au nom d'une propriété de votre modèle:

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

Supprimez également le paramètre htmlAttributes de votre appel BeginForm, car cela ne fait rien d'utile. Il devient:

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

Toutes les données renvoyées par le client auraient pu être falsifiées, vous devez donc uniquement renvoyer l'ID unique de la transaction, puis récupérer toutes les informations supplémentaires à ce sujet dans votre source de données pour effectuer un traitement ultérieur. Vous voudrez également vérifier ici que l'utilisateur qui publie les données a accès à l'ID de transaction spécifié, car cela aurait également pu être falsifié.

22
Jonathan Little

Ce n'est pas spécifique à MVC. Le formulaire HTML ne publiera que les valeurs contenues dans les éléments du formulaire à l'intérieur du formulaire. Votre exemple n'est ni à l'intérieur du formulaire ni dans un élément de formulaire (comme les entrées masquées). Vous devez le faire car MVC ne dépend pas de l'état d'affichage. Mettez les champs cachés à l'intérieur le formulaire:

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

Demandez-vous cependant .. si l'utilisateur ne met pas à jour ces valeurs .. votre méthode d'action les requiert-elle?

11
Simon Whitehead

La liaison de modèle hydrate votre modèle de vue dans l'action de votre contrôleur via des valeurs de formulaire publiées. Je ne vois aucun contrôle de formulaire pour vos variables susmentionnées, donc rien ne serait affiché. Pouvez-vous voir si cela vous plaît?

@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" })
}
9
Mister Epic

Essayez de faire une boucle avec l'instruction suivante et non avec 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>
3
Valynk

Essayez les collections de formulaires et obtenez la valeur as. Je pense que cela peut fonctionner.

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

Le nom de l'action vers laquelle les données seront publiées doit être identique au nom de l'action à partir de laquelle les données sont publiées. La seule différence devrait être que la deuxième action où les données sont publiées doit avoir [HttpPost] et la méthode de publication ne doit servir que les requêtes Get.

0
Bounty

Pouvez-vous simplement combiner ces 2 modèles que vous avez? Voici comment je le fais avec un modèle par vue ... 1. J'utilise les modèles d'affichage de vue en vue afin de pouvoir passer tout le modèle et laisser les données cryptées. 2. Configurez votre vue principale comme ceci ...

@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. Créez un dossier DisplayTemplates dans partagé. Créez une vue, en la nommant comme votre modèle que vous voulez passer car un DisplayFor recherche le modèle d'affichage nommé d'après le modèle que vous utilisez, j'appelle le mien GroupModel. Considérez un modèle d'affichage comme une instance d'objet de votre énumération. Groupmodel ressemble à ceci, j'assigne simplement un groupe à un bouton.

@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. Voici le contrôleur. * 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. Maintenant, si j'ai beaucoup de vues avec différents modèles, j'utilise généralement un modèle lié à la vue, puis un obj de session qui récupère les données de chaque modèle, donc à la fin j'ai des données à soumettre.

0
foxtrotZulu

Mettez tous les champs à l'intérieur du formulaire

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

et assurez-vous que le modèle

 BitcoinTransactionViewModel

inclus en vue ou non?

0
Kalyan