Bassie
Bassie

Reputation: 10400

How to populate the properties of a collection inside a model

I have a model, Sample which contains a List<QCItem>, which is populated in the constructor.

In my Sample.Create.cshtml form, I have the following section, for populating the Mass values of each item in a List<QCItem>in the model:

@for (var itemCnt = 0; itemCnt < Model.Defects.Count(); itemCnt++)
{
    @Html.LabelFor(m => Model.Defects[itemCnt], Model.Defects[itemCnt].Criteria)
    @Html.HiddenFor(m => Model.Defects[itemCnt].Criteria, 
        new { @value = Model.Defects[itemCnt].Criteria })

    @Html.TextBoxFor(m => Model.Defects[itemCnt].Mass)
    @Html.HiddenFor(m => Model.Defects[itemCnt].Mass)
}

While debugging, I can see that each item in Model.Defects in the controller for Model contains the Mass value I entered in the TextBoxFor, and also that each of these textboxes have the correct label next to them.

However, I also need each of these QCItems to have Criteria assigned, which is why I added the HiddenFor field above, but when the debugger hits the controller, each of the QCItems has a Criteria value of null.

How can I pass this value to the controller action in the same way that Mass is being passed? (It should automatically be assigned to the correct QCItem) The value definitely exists, as it is displayed in the LabelFor.

Update

This is the method in SampleController:

public ActionResult Insert(HttpPostedFileBase file, Sample sample)
{
    SelectedPallet = (Pallet)Session["pallet"];
    sample.Pallet = SelectedPallet;

    var access = new Access();
    access.InsertNewQCSample(sample);

    var files = Request.Files;
    // Insert into database

    return View("Details");
}

Here is the Sample model with all of its properties:

public class Sample : IGriddable
{
    private DataRow dataRow;
    private DataRow row;

    public string[] ColumnHeaders { get; } = new string[] { "SampleNumber", "Date", "ClerkStatus", "SupervisorStatus" };
    public string RowLinkPrefix { get { return $"/receiving/{Pallet.Grv.GrvNumber}/{Pallet.PalletSequence}/"; } }
    public bool Selectable { get; } = true;

    public Pallet Pallet { get; set; }
    public string Clerk { get; set; }
    public string MassOff { get; set; }
    public string ProductionComment { get; set; }
    public string Quantity { get; set; }
    public string Supervisor { get; set; }
    public string TechnicalComment { get; set; }

    public string SampleNumber { get; set; }
    public string ClerkStatus { get; set; }
    public DateTime Date { get; set; }
    public string SupervisorStatus { get; set; }

    public string ProductSpec { get; set; }

    // Each defect status needs to be saved as a (DB)subQCItems item
    public List<QCItem> Defects { get; set; } = new List<QCItem>();
    public List<string> ImagePaths { get; set; } = new List<string>();
}

Here is the QCItem model:

public class QCItem : IGriddable
{
    public string[] ColumnHeaders { get; } = new string[] { "Criteria", "Major", "Mass" };
    public string RowLinkPrefix { get; } = string.Empty;
    public bool Selectable { get; } = false;

    public string Criteria { get; internal set; }
    public bool Major { get; set; }
    public int Mass { get; set; }

    public Sample Sample;
}

Upvotes: 2

Views: 80

Answers (2)

user3559349
user3559349

Reputation:

You have given your Criteria property and internal setter and the DefaultModelBinder cannot set it (it needs to be public)

public string Criteria { get; set; }

Side notes:

Your LabelFor() method is not associating a the label with the textbox (clicking on it will not set focus. It needs to be

@Html.LabelFor(m => Model.Defects[itemCnt].Mass, Model.Defects[itemCnt].Criteria)

Your hidden input for Criteria should be just

@Html.HiddenFor(m => Model.Defects[itemCnt].Criteria)

The hidden input for Mass is pointless and its value will be ignored by the DefaultModelBinder because your have a TextBoxFor() for the same property before the hidden input

Upvotes: 1

Bassie
Bassie

Reputation: 10400

Thanks to the help of Stephen Muecke's comment (which gave a suggestion which I had already tried), I noticed that the Criteria property had an internal setter, which cannot be accessed from the view.

After changing this to

public string Criteria { get; set; }

and in the form:

@for (var itemCnt = 0; itemCnt < Model.Defects.Count(); itemCnt++)
{
    @Html.LabelFor(m => Model.Defects[itemCnt], Model.Defects[itemCnt].Criteria)
    @Html.HiddenFor(m => Model.Defects[itemCnt].Criteria)

    @Html.TextBoxFor(m => Model.Defects[itemCnt].Mass)
}

It now works fine

Upvotes: 0

Related Questions