2013-04-09 2 views
4

J'essaie d'utiliser Steve Sanderson's blog post concernant les éléments de collection de liaison à un modèle. Cependant, je vois un comportement étrange que je ne trouve pas de réponse dans le post ou d'autres discussions. Dans mon modèle BankListMaster, j'ai un objet ICollection d'un modèle séparé BankAgentId. BankListMaster et BankListAgentId ont une relation un-à-plusieurs dans notre base de données SQL.ASP MVC3 - BeginCollectionItem renvoyant des valeurs nulles

Je suis confronté au problème sur la page Edit. Lorsque la page se charge, les trois ID d'agent que nous avons actuellement associés à l'article BankListMaster que je travaille avec charger correctement. Cependant, si je clique sur "Sauvegarder", je vois que l'objet ICollection (bankListAgentId) compte trois éléments, mais chaque champ respectif contient une valeur null.

Si je sélectionne le Add another, puis, en suivant les instructions sur le blog post, l'Ajax appelle une vue partielle qui est correctement ajoutée à la table.

Maintenant, si je frappe « Enregistrer », je vois que le nombre d'objets ICollection a augmenté d'un point à un compte de 4. Tous les éléments qui ont été initialement chargés avec le GET contiennent à nouveau null valeurs de champ, mais le AgentId et StateCode les champs de l'élément nouvellement ajouté contiennent les informations correctes (tous les autres champs pour le nouvel élément sont null, cependant).

Encore relativement nouveau pour ASP MVC, donc je ne suis pas sûr de ce qui se passe ou quelle direction regarder.

Voici le formulaire de la vue principale. J'ai essayé ceci avec et sans les articles @Html.Hidden et ai reçu les mêmes résultats.

@model Monet.Models.BankListMaster 

@{ 
    ViewBag.Title = "Edit"; 
} 
    <fieldset> 
     <legend>Stat(s) Fixed</legend> 
     <table id="fixedRows"> 
      <thead> 
       <tr> 
        <th>State Code</th> 
        <th>Agent ID</th> 
        <th></th> 
        <th></th> 
       </tr> 
      </thead> 
      <tbody> 

       @for (int i = 0; i < Model.Fixed.Count; i++) 
       { 
        using (Html.BeginCollectionItem("BankListAgentId")) 
        {       
          @Html.HiddenFor(m => Model.Fixed[i].BankID) 
          @Html.HiddenFor(m => Model.Fixed[i].TableId) 
          @Html.HiddenFor(m => Model.Fixed[i].FixedOrVariable) 
          <tr> 
           <td> 
            @Html.DropDownListFor(m => Model.Fixed[i].StateCode, 
             (SelectList)ViewBag.StateCodeList, Model.Fixed[i].StateCode) 
           </td> 
           <td> 
            @Html.TextBoxFor(m => Model.Fixed[i].AgentId) 
            @Html.ValidationMessageFor(m => Model.Fixed[i].AgentId) 
           </td> 
           <td> 
            <a href="javascript:void(0)" class="deleteRow">delete</a> 
           </td> 
           @*<td><a href="#" onclick="$('#[email protected]').parent().remove();" style="float:right;">Delete</a></td>*@ 
          </tr> 

        } 
       } 

      </tbody> 

     </table> 
     <br /> 
     <a href="javascript:void(0)" class="addFixed">Add Another</a> 
    </fieldset> 

Voici la vue partielle. Encore une fois, j'ai essayé avec et sans les articles @Html.Hidden et j'ai reçu le même résultat.

@model Monet.Models.BankListAgentId 

@{ 
    Layout = null; 
} 
@using (Html.BeginCollectionItem("BankListAgentId")) 
{ 
    @Html.HiddenFor(model => model.TableId)   
    @Html.HiddenFor(model => model.BankID) 
    @Html.HiddenFor(model => model.FixedOrVariable) 

    <tr> 
     <td> 
      @Html.DropDownListFor(model => model.StateCode, 
       (SelectList)ViewBag.StateCodeList, Model.StateCode) 
     </td> 
     <td> 
      @Html.EditorFor(model => model.AgentId) 
      @Html.ValidationMessageFor(model => model.AgentId) 
     </td> 
     <td> 
      <a href="javascript:void(0)" class="deleteRow">delete</a> 

     </td> 
     @*<td><a href="#" onclick="$('#[email protected]').parent().remove();" style="float:right;">Delete</a></td>*@ 
    </tr> 
} 

Voici l'appel Ajax

$(document).ready(function() { 

    $(".addFixed").click(function() { 
     $.ajax({ 
      url: '@Url.Action("BlankFixedRow", "BankListMaster")', 
      dataType: 'html', 
      cache: false, 
      success: function (html) { 
       $("#fixedRows > tbody").append('<tr>' + html + '</tr>'); 
      } 
     }); 
    }); 
}); 

Voici la méthode du contrôleur qui appelle la vue partielle

public ViewResult BlankFixedRow() 
    { 
     SelectList tmpList = new SelectList(new[] { "AL", "AK", "AS", "AZ", "AR", "CA", "CO", "CT", "DE", "DC", "FM", "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MH", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NA", "NM", "NY", "NC", "ND", "MP", "OH", "OK", "OR", "PW", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "UT", "US", "VT", "VI", "VA", "WA", "WV", "WI", "WY" }); 
     ViewBag.StateCodeList = tmpList; 

     return View("FixedPartialView", new BankListAgentId()); 
    } 

Ce modèle est le BankListMaster

public partial class BankListMaster 
{ 
    public BankListMaster() 
    { 
     this.BankListAttachments = new HashSet<BankListAttachments>(); 
     this.BankListAgentId = new HashSet<BankListAgentId>(); 
    } 

    public int ID { get; set; } 
    public string BankName { get; set; } 
    public string LastChangeOperator { get; set; } 
    public Nullable<System.DateTime> LastChangeDate { get; set; } 

    public virtual ICollection<BankListAttachments> BankListAttachments { get; set; } 
    public virtual ICollection<BankListAgentId> BankListAgentId { get; set; } 
} 

Et ce est le BankListAgentId modèle

public partial class BankListAgentId 
{ 
    public string AgentId { get; set; } 
    public int BankID { get; set; } 
    public string FixedOrVariable { get; set; } 
    public string StateCode { get; set; } 
    public int TableId { get; set; } 

    public virtual BankListMaster BankListMaster { get; set; } 
} 

Voici le contenu envoyé par le formulaire sur le poste au contrôleur via Fiddler. Les éléments indexés 1, 2 et 3 sont les éléments extraits de la base de données. Le dernier élément a été ajouté via l'appel jQuery/Ajax à la vue partielle.

enter image description here

+0

Cela devient de plus en plus compliqué Neal :) Tout d'abord, vous aurez besoin de partager l'action de poste de contrôleur, nous avons donc une idée de ce que vous « attraper "dans votre action. Deuxièmement, utilisez-vous fiddler? Il vous serait très utile de voir ce qui est affiché sur votre contrôleur. –

+0

@ Mariusz.W: dites-moi à ce sujet .... pic Fiddler ajouté par demande. – NealR

+0

Le dernier élément de cette liste de Fiddler contient 2 fois le champ FixedOrVariable avec une clé identique. Il n'y a pas de champ TableId cependant. Je ne sais pas pourquoi c'est le cas puisque votre vue partielle que vous avez posté ci-dessus semble bien, mais juste pensé qu'il valait la peine de souligner ... – Marko

Répondre

1

Ma solution était: tout utilisé dans le html.beginCollectionItem doit être dans une vue partielle. donc votre solution pourrait être quelque chose comme ceci:

Vue principale

@model Monet.Models.BankListMaster 

@{ 
    ViewBag.Title = "Edit"; 
} 
    <fieldset> 
     <legend>Stat(s) Fixed</legend> 
     <table id="fixedRows"> 
     <thead> 
       <tr> 
        <th>State Code</th> 
        <th>Agent ID</th> 
        <th></th> 
        <th></th> 
      </tr> 
     </thead> 
     <tbody> 

      @for (int i = 0; i < Model.Fixed.Count; i++) 
      { 
       @Html.Partial("name", item)  
      } 

     </tbody> 

    </table> 
    <br /> 
    <a href="javascript:void(0)" class="addFixed">Add Another</a> 
</fieldset> 

Vue partielle « nom »

@model Monet.Models.BankListMaster 

using (Html.BeginCollectionItem("BankListAgentId")) 
{       
     @Html.HiddenFor(m => Model.Fixed[i].BankID) 
     @Html.HiddenFor(m => Model.Fixed[i].TableId) 
     @Html.HiddenFor(m => Model.Fixed[i].FixedOrVariable) 
     <tr> 
      <td> 
       @Html.DropDownListFor(m => Model.Fixed[i].StateCode, 
        (SelectList)ViewBag.StateCodeList, Model.Fixed[i].StateCode) 
      </td> 
      <td> 
       @Html.TextBoxFor(m => Model.Fixed[i].AgentId) 
       @Html.ValidationMessageFor(m => Model.Fixed[i].AgentId) 
      </td> 
      <td> 
       <a href="javascript:void(0)" class="deleteRow">delete</a> 
      </td> 
      @*<td><a href="#" onclick="$('#[email protected]').parent().remove();" style="float:right;">Delete</a></td>*@ 
      </tr> 

} 

Ce n'est pas la solution complète, mais vous devez le faire comme ça, ça a marché pour moi. Espérons que cela aide!

+0

Comment avez-vous fait passer le compteur dans la vue partielle? – NealR

0

J'ai été bloqué sur un problème similaire pendant plusieurs heures. Ma collection d'éléments (Choices) renverrait null s'il contenait plus d'un élément. Cependant, comme vous, mes données semblaient être rendu très bien:

{ 
    "QuestionTemplateId":"1", 
    "Position":"0", 
    "CardId":"1", 
    "Label":"Question#1", 
    "AdminComments":"", 
    "Type":"ComboBox", 
    "ModelType":"MyProject.Areas.DGM.Models.ViewModels.Controls.ComboBoxViewModel", 
    "ComboQuestionId":"1", 
    "Choices.Index": ["dc0e6eea-5a8e-4971-8f9f-4d6e1c290300","52f2b780-c21e-4633-b880-bdff5d815eaf"], 
    "Choices[dc0e6eea-5a8e-4971-8f9f-4d6e1c290300].Label":"Choice #1", 
    "Choices[dc0e6eea-5a8e-4971-8f9f-4d6e1c290300].Score":"4", 
    "Choices[52f2b780-c21e-4633-b880-bdff5d815eaf].Label":"Choice #2", 
    "Choices[52f2b780-c21e-4633-b880-bdff5d815eaf].Score":"7" 
} 

Je me suis alors rendu compte que mon problème peut être connecté au poste de données. Lorsque l'utilisateur soumet le formulaire, je fais l'appel AJAX suivant:

$("#questionForm").on('submit', function() { 
    if ($(this).valid()) { 
     var data = $(this).serializeObject(); // from here: http://stackoverflow.com/a/1186309/2835243 
     $.ajax({ 
      type: 'POST', 
      url: this.action, 
      contentType: 'application/json', 
      data: JSON.stringify(data) // this is what generated the above JSON 
     }); 
    } 
} 

Ce qui semblait fixer pour moi est d'envoyer des données x-www-form-urlencoded (la valeur par défaut contentType pour AJAX jQuery) au lieu de json. Alors je l'ai changé mon appel AJAX pour ce qui suit:

$.ajax({ 
    type: 'POST', 
    url: this.action, 
    data: $(this).serialize() 
}); 
Questions connexes