简体   繁体   中英

ASP NET MVC 4 collection is null on post

I read most of Google :-), but I can't proceed. The collection on my object is and stays null on post, whatever I do.

My Model:

public class ArticleViewModel
{
    public Guid EventId { get; set; }
    public IList<ArticleItemViewModel> ArtikelListe { get; set; }

    public decimal GesamtpreisNetto { get; set; }
    public decimal MwSt { get; set; }
}

and

public class ArticleItemViewModel
{
    public Guid EventId { get; set; }
    public Guid Id { get; set; }

    public string Artikelname { get; set; }
    public string Artikelname_EN { get; set; }

    public string Information { get; set; }
    public string Information_EN { get; set; }

    public decimal Preis { get; set; }

    public bool MitAnzahl { get; set; }
    public bool IstKategorie { get; set; }

    public int Anzahl { get; set; }
    public bool Checkbox { get; set; }

    public int Reihenfolge { get; set; }
}

My View:

@using (Html.BeginForm("Next", "Article", FormMethod.Post))
{
@Html.HiddenFor(x => x.EventId)

<input type="hidden" name="ArtikelListe" />

for (var i = 0; i < Model.ArtikelListe.Count; i++)
//  foreach (EventManager.ViewModels.ArticleItemViewModel artikelItem in Model.ArtikelListe)
{               
    <div>
        <div>
            @if (Model.ArtikelListe[i].IstKategorie)
            {
                @Html.LabelFor(x => x.ArtikelListe[i].Artikelname)<br />  
                @Html.LabelFor(x => x.ArtikelListe[i].Information)
            }
            else
            {
                if (Model.ArtikelListe[i].MitAnzahl)
                {
                    @Html.TextBoxFor(x => x.ArtikelListe[i].Anzahl, new { @class = "field text fn" })
                }
                else
                {
                    @Html.LabelFor(x => x.ArtikelListe[i].Anzahl)                     
                }

                @Html.LabelFor(x => x.ArtikelListe[i].Artikelname)<br />  
                @Html.LabelFor(x => x.ArtikelListe[i].Information)
            }        
        </div>
    </div>
}

On post, I get my Viewmodel back and it has a Collection of ArtikelListe with 15 items (thats correct), but these are all null!

In my HTTP Header I get the following post data:

EventId:824e7f3c-7190-4ebb-aa60-51b57c977b1e
ArtikelListe:
ArtikelListe[1].Anzahl:0
ArtikelListe[2].Anzahl:1
ArtikelListe[3].Anzahl:0
submitButton:Nächste

I wonder why just partial data is sent withon the http post and why all my list items are null. I tried to render by for and foreach. same result.

Any ideas? I'm helpless.

The issue in this line:

<input type="hidden" name="ArtikelListe" />

When POST request is sent back:

ArtikelListe:
ArtikelListe[1].Anzahl:0
ArtikelListe[2].Anzahl:1
ArtikelListe[3].Anzahl:0

Then ArtikelListe overrides value for the list and that's why it's always null. So you just need to rename your hidden field to some another name to not conflict with existing names.

Here is working example based on your MVC code in DotNetFiddle - https://dotnetfiddle.net/BCXduq

You can click RUN, then enter some values in two input fields in the right bottom box, and click Save button. And then it will display model that server received in POST as JSON text.

Collection indexers must start at zero and be consecutive (unless you include an Index property).Because of your if statements, you are not necessarily generating a control for the property Anzahl . Looking at your header information, you do not have a value for ArtikelListe[0].Anzahl which means that the first item must have either IstKategorie=true or MitAnzahl=false . You can correct this by adding a hidden input so a value posts back

@if (Model.ArtikelListe[i].IstKategorie)
{
  @Html.LabelFor(x => x.ArtikelListe[i].Artikelname)<br />  
  @Html.LabelFor(x => x.ArtikelListe[i].Information)
  @Html.HiddenFor(x => x.ArtikelListe[i].Anzahl) // add this
}
else
{
  if (Model.ArtikelListe[i].MitAnzahl)
  {
    @Html.TextBoxFor(x => x.ArtikelListe[i].Anzahl, new { @class = "field text fn" })
  }
  else
  {
    @Html.LabelFor(x => x.ArtikelListe[i].Anzahl)
    @Html.HiddenFor(x => x.ArtikelListe[i].Anzahl) // add this         
  }
  @Html.LabelFor(x => x.ArtikelListe[i].Artikelname)<br />  
  @Html.LabelFor(x => x.ArtikelListe[i].Information)
}

Alternatively you can add an Index property which the DefaultModelBinder uses to match up collection items that are non-consecutive

@if (Model.ArtikelListe[i].IstKategorie)
{
  @Html.LabelFor(x => x.ArtikelListe[i].Artikelname)<br />  
  @Html.LabelFor(x => x.ArtikelListe[i].Information)
}
else
{
  if (Model.ArtikelListe[i].MitAnzahl)
  {
    @Html.TextBoxFor(x => x.ArtikelListe[i].Anzahl, new { @class = "field text fn" })
    <input type="hidden" name="x.ArtikelListe.Index" value="@i" /> // add this manually
  }
  else
  {
    @Html.LabelFor(x => x.ArtikelListe[i].Anzahl)     
  }
  @Html.LabelFor(x => x.ArtikelListe[i].Artikelname)<br />  
  @Html.LabelFor(x => x.ArtikelListe[i].Information)
}

With the first option, it will post back all items. In the second case it will post back only items that meet the if conditions.

Note as Sergey noted, you need to also remove <input type="hidden" name="ArtikelListe" />

Thank you guys.

The problem was, that I did the testing with the script from the Fiddle. In this code

Model.ArtikelListe[i].MitAnzahl

was always true.

In case it is not true, the value "Anzahl" was not bound to a control containing a value (but just a label).

@Html.LabelFor(x => x.ArtikelListe[i].Anzahl)

As soon as I inserted a hidden field within that scope and bound the "Anzahl" value to it, the post returned with all the data I expected.

Thanks anyway. I learned a lot!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM