web-dev-qa-db-fra.com

POST un tableau de formulaires sans succès

Je développe un site Web ASP.NET MVC 5 avec C # et .NET Framework 4.5.1.

J'ai ce form dans un fichier cshtml:

@model MyProduct.Web.API.Models.ConnectBatchProductViewModel

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Create</title>
</head>
<body>
    @if (@Model != null)
    { 
        <h4>Producto: @Model.Product.ProductCode, Cantidad: @Model.ExternalCodesForThisProduct</h4>
        using (Html.BeginForm("Save", "ConnectBatchProduct", FormMethod.Post))
        {
            @Html.HiddenFor(model => model.Product.Id, new { @id = "productId", @Name = "productId" });

            <div>
                <table id ="batchTable" class="order-list">
                    <thead>
                        <tr>
                            <td>Cantidad</td>
                            <td>Lote</td>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>@Html.TextBox("ConnectBatchProductViewModel.BatchProducts[0].Quantity")</td>
                            <td>@Html.TextBox("ConnectBatchProductViewModel.BatchProducts[0].BatchName")</td>
                            <td><a class="deleteRow"></a></td>
                        </tr>
                    </tbody>
                    <tfoot>
                        <tr>
                            <td colspan="5" style="text-align: left;">
                                <input type="button" id="addrow" value="Add Row" />
                            </td>
                        </tr>
                    </tfoot>
                </table>
            </div>
            <p><input type="submit" value="Seleccionar" /></p>
        }
    }
    else
    { 
        <div>Error.</div>
    }
<script src="~/Scripts/jquery-2.1.3.min.js"></script>
<script src="~/js/createBatches.js"></script> <!-- Resource jQuery -->    
</body>
</html>

Et voici la méthode d'action:

[HttpPost]
public ActionResult Save(FormCollection form)
{
    return null;
}

Et les deux ViewModel:

public class BatchProductViewModel
{
    public int Quantity { get; set; }
    public string BatchName { get; set; }
}

public class ConnectBatchProductViewModel
{
    public Models.Products Product { get; set; }
    public int ExternalCodesForThisProduct { get; set; }

    public IEnumerable<BatchProductViewModel> BatchProducts { get; set; }
}

Mais je reçois ceci dans FormCollection form var: enter image description here

Mais je veux obtenir un IEnumerable<BatchProductViewModel> model:

public ActionResult Save(int productId, IEnumerable<BatchProductViewModel> model);

Si j'utilise la signature de méthode ci-dessus, les deux paramètres sont nuls.

Je veux un IEnumerable parce que l'utilisateur va ajouter plus de lignes dynamiquement en utilisant jQuery.

Voici le script jQuery:

jQuery(document).ready(function ($) {
    var counter = 0;

    $("#addrow").on("click", function () {

        counter = $('#batchTable tr').length - 2;

        var newRow = $("<tr>");
        var cols = "";

        var quantity = 'ConnectBatchProductViewModel.BatchProducts[0].Quantity'.replace(/\[.{1}\]/, '[' + counter + ']');
        var batchName = 'ConnectBatchProductViewModel.BatchProducts[0].BatchName'.replace(/\[.{1}\]/, '[' + counter + ']');

        cols += '<td><input type="text" name="' + quantity + '"/></td>';
        cols += '<td><input type="text" name="' + batchName + '"/></td>';

        cols += '<td><input type="button" class="ibtnDel"  value="Delete"></td>';
        newRow.append(cols);

        $("table.order-list").append(newRow);
        counter++;
    });


    $("table.order-list").on("click", ".ibtnDel", function (event) {
        $(this).closest("tr").remove();

        counter -= 1
        $('#addrow').attr('disabled', false).prop('value', "Add Row");
    });
});

Une idée?

J'ai vérifié ceci réponse SO , et cet article mais je ne fais pas fonctionner mon code.

39
VansFannel

Vous devez générer les contrôles de la collection dans une boucle for afin qu'ils soient correctement nommés avec des indexeurs (notez que la propriété BatchProducts doit être IList<BatchProductViewModel>

@using (Html.BeginForm("Save", "ConnectBatchProduct", FormMethod.Post))
{
  ....
  <table>
    ....
    @for(int i = 0; i < Model.BatchProducts.Count; i++)
    {
      <tr>
        <td>@Html.TextBoxFor(m => m.BatchProducts[i].Quantity)</td>
        <td>@Html.TextBoxFor(m => m.BatchProducts[i].BatchName)</td>
        <td>
          // add the following to allow for dynamically deleting items in the view
          <input type="hidden" name="BatchProducts.Index" value="@i" />
          <a class="deleteRow"></a>
        </td>
      </tr>
    }
    ....
  </table>
  ....
}

Ensuite, la méthode POST doit être

public ActionResult Save(ConnectBatchProductViewModel model)
{
  ....
}

Modifier

Remarque: Suite à votre modification, si vous souhaitez ajouter et supprimer dynamiquement des éléments BatchProductViewModel dans la vue, vous devrez utiliser l'assistant BeginCollectionItem ou un modèle html comme indiqué dans cette réponse

Le modèle pour ajouter dynamiquement de nouveaux éléments serait

<div id="NewBatchProduct" style="display:none">
  <tr>
    <td><input type="text" name="BatchProducts[#].Quantity" value /></td>
    <td><input type="text" name="BatchProducts[#].BatchName" value /></td>
    <td>
      <input type="hidden" name="BatchProducts.Index" value ="%"/>
      <a class="deleteRow"></a>
    </td>
  </tr>
</div>

Notez que les indexeurs factices et la valeur non correspondante pour l'entrée masquée empêchent la publication de ce modèle.

Ensuite, le script pour ajouter un nouveau BatchProducts serait

$("#addrow").click(function() {
  var index = (new Date()).getTime(); // unique indexer
  var clone = $('#NewBatchProduct').clone(); // clone the BatchProducts item
  // Update the index of the clone
  clone.html($(clone).html().replace(/\[#\]/g, '[' + index + ']'));
  clone.html($(clone).html().replace(/"%"/g, '"' + index  + '"'));
  $("table.order-list").append(clone.html());
});
31
user3559349

En suivant le principe de DRY, vous pouvez créer un EditorTemplate à cet effet. Pas:

1- Dans Vues> Partagé> Créer un nouveau dossier nommé (EditorTemplates)

2- Créez une vue à l'intérieur de votre dossier EditorTemplates nouvellement créé, le modèle de la vue doit être BatchProductViewModel selon l'exemple OP. Placez votre code dans la vue Editeur. Aucune boucle ou index n'est requis.

n EditorTemplate agira de manière similaire à une PartialView pour chaque entité enfant mais d'une manière plus générique.

3- Dans la vue de votre entité parent, appelez votre éditeur: @ Html.EditorFor (m => m.BatchProducts)

Non seulement cela fournit des vues plus organisées, mais nous vous permettons également de réutiliser le même éditeur dans d'autres vues.

0
Mohamed Nagieb

vous pouvez visiter cet article pour le code source complet avec un tutoriel vidéo .

vous devez d'abord créer une action, d'où nous pouvons passer la liste des objets

[HttpGet]
public ActionResult Index()
{
    List<Contact> model = new List<Contact>();
    using (MyDatabaseEntities dc = new MyDatabaseEntities())
    {
        model = dc.Contacts.ToList();
    }
    return View(model);
}

alors nous devons créer une vue pour cette action

@model List<UpdateMultiRecord.Contact>
@{
    ViewBag.Title = "Update multiple row at once Using MVC 4 and EF ";
}
@using (@Html.BeginForm("Index","Home", FormMethod.Post))
{
    <table>
            <tr>
                <th></th>               
                <th>Contact Person</th>
                <th>Contact No</th>
                <th>Email ID</th>
            </tr>
        @for (int i = 0; i < Model.Count; i++)
        {
            <tr>               
                <td> @Html.HiddenFor(model => model[i].ContactID)</td>
                <td>@Html.EditorFor(model => model[i].ContactPerson)</td>
                <td>@Html.EditorFor(model => model[i].Contactno)</td>
                <td>@Html.EditorFor(model => model[i].EmailID)</td>
            </tr>
        }
    </table>
    <p><input type="submit" value="Save" /></p>
    <p style="color:green; font-size:12px;">
        @ViewBag.Message
    </p>
}
 @section Scripts{
    @Scripts.Render("~/bundles/jqueryval")
 }

puis nous devons écrire du code pour enregistrer la liste des objets dans la base de données

[HttpPost]
public ActionResult Index(List<Contact> list)
{  
    if (ModelState.IsValid)
    {
        using (MyDatabaseEntities dc = new MyDatabaseEntities())
        {
            foreach (var i in list)
            {
                var c = dc.Contacts.Where(a =>a.ContactID.Equals(i.ContactID)).FirstOrDefault();
                if (c != null)
                {
                    c.ContactPerson = i.ContactPerson;
                    c.Contactno = i.Contactno;
                    c.EmailID = i.EmailID;
                }
            }
            dc.SaveChanges();
        }
        ViewBag.Message = "Successfully Updated.";
        return View(list);
    }
    else
    {
        ViewBag.Message = "Failed ! Please try again.";
        return View(list);
    }
}
0
Sourav Mondal
using(Html.BeginForm())
{
  // code here 

}

Pendant la publication des données de formulaire, toutes les balises doivent être incluses dans la balise de formulaire.

0
Mahesh Pullagura

Dans votre méthode de publication, vous recevez "MyProduct.Web.API.Models.ConnectBatchProductViewModel" comme paramètre.
Utilisez le modèle existant pour la méthode Post.

Pourquoi voulez-vous un IEnumerable de votre modèle? il n'y en a qu'un seul, y compris l'ID dans le modèle.

0
Andre