0

I have an ASP.NET MVC application that tracks vendors for our company by purchase orders. Each PO rating is assigned a type. Each type can have multiple rating sections and each rating section can have multiple categories. The classes look like that:

public class RatingModel{
    /*Fields for the purchase order*/

    public List<RatingSectionModel> RatingSections { get; set; }
}

public class RatingSectionModel {
    /*Fields for the Rating section*/

    public List<RatingCategoryModel > Categories { get; set; }
}

public class RatingCategoryModel {
    /*Fields for the category*/
}

In my project, I am attempting to use the modified BeginCollectionItem found here. The cshtml is:

_RatingModel.cshtml:

@model VendorRating.Models.RatingModel
@Html.HiddenFor(model => model.VendorId)
@Html.HiddenFor(model => model.VendorName)
@Html.HiddenFor(model => model.AwardDate)
@Html.HiddenFor(model => model.AwardValue)
@Html.HiddenFor(model => model.CloseDate)
@Html.HiddenFor(model => model.FinalValue)
@Html.HiddenFor(model => model.ServiceRep)
@Html.HiddenFor(model => model.PoId)

<div style="display:table;margin-bottom:5px;">
    <div style="display:table-row">
        <div style="display:table-cell;vertical-align:top;">
            @Html.LabelFor(model => model.TypeId)
        </div>
        <div style="display:table-cell;vertical-align:top;">
            @Html.DropDownListFor(model => model.TypeId, Model.Types)
        </div>
    </div>
</div>
<table>
    <tr>
        <td class="table-header">@Html.LabelFor(model => model.PoId, htmlAttributes: new { @class = "control-label" })</td>
        <td class="table-header">@Html.LabelFor(model => model.VendorName, htmlAttributes: new { @class = "control-label" })</td>
        <td class="table-header">@Html.LabelFor(model => model.AwardDate, htmlAttributes: new { @class = "control-label" })</td>
        <td class="table-header">@Html.LabelFor(model => model.AwardValue, htmlAttributes: new { @class = "control-label" })</td>
    </tr>
    <tr>
        <td>@Html.DisplayFor(model => model.PoId, new { htmlAttributes = new { @class = "form-control" } })</td>
        <td>@Html.DisplayFor(model => model.VendorName, new { htmlAttributes = new { @class = "form-control" } })</td>
        <td>@Html.DisplayFor(model => model.AwardDate, new { htmlAttributes = new { @class = "form-control" } })</td>
        <td>@Html.DisplayFor(model => model.AwardValue, new { htmlAttributes = new { @class = "form-control" } })</td>
    </tr>
    <tr>
        <td class="table-header">@Html.LabelFor(model => model.CloseDate, htmlAttributes: new { @class = "control-label" })</td>
        <td class="table-header" ">@Html.LabelFor(model => model.FinalValue, htmlAttributes: new { @class = "control-label" })</td>
        <td class="table-header" ">@Html.LabelFor(model => model.ServiceRep, htmlAttributes: new { @class = "control-label" })</td>
    </tr>
    <tr>
        <td>@Html.DisplayFor(model => model.CloseDate, new { htmlAttributes = new { @class = "form-control" } })</td>
        <td>@Html.DisplayFor(model => model.FinalValue, new { htmlAttributes = new { @class = "form-control" } })</td>
        <td>@Html.DisplayFor(model => model.ServiceRep, new { htmlAttributes = new { @class = "form-control" } })</td>
    </tr>
</table>
<div id="sections">
    @{
        if(Model.Sections != null) {
            @Html.EditorFor(model => model.Sections)

        }
    }

RatingSectionModel.cshtml:

@model List<VendorRating.Models.RatingSectionModel>

@{
    var id = "";

    foreach(VendorRating.Models.RatingSectionModel sectionModel in Model) {
        using(Html.BeginCollectionItem("Sections", out id)) {

            <fieldset style="margin-top:10px;">
                <legend style="font-weight:bold;">
                    @sectionModel.SectionName
                    @Html.HiddenFor(model => sectionModel.SectionId)
                </legend>
                <div style="display:table;width:100%">

                    @foreach(VendorRating.Models.RatingCategoryModel c in sectionModel.RatingCategories) {
                        @Html.EditorFor(cat => c);
                    }
                    <div style="display:table-row;width:100%">
                        <div style="display:table-cell;width:70%"></div>
                        <div style="display:table-cell;text-align:right;width:10%"></div>
                        <div style="display:table-cell;text-align:right;width:10%">Total</div>
                        <div style="display:table-cell;text-align:right;width:10%;"><div data-section-total="@sectionModel.SectionId" style="width:50%;border-bottom:solid 1px lightgray;text-align:right;"></div></div>
                    </div>
                </div>
            </fieldset>
            <script>
                function changeTotals() {
                    var totalScore = 0;
                    $('#poForm').find('*[data-section-score="@sectionModel.SectionId"]').each(function () {
                        var val = parseFloat($(this).text());

                        if (!isNaN(val)) {
                            totalScore = totalScore + val;
                        }
                    });

                    $('#poForm').find('*[data-section-total="@sectionModel.SectionId"]').text(totalScore);
                }
            </script>
        }
    }
}

The fields are generated correctly, but when I examine the form, the name of each field is Sections[sectionid here].sectionModel.FieldName. The sectionModel being inserted into the name is keeping it from binding properly with the Sections collection when the form is submitted. The proper number of sections are added to the collection, but all of their properties are empty. I'm sure I have something set up wrong here, but I have no idea what it could be.

6
  • id values are not used in binding - they are not submitted to server. It is name html attributes that are keys in the Request.Form collection, and binding uses them. Commented Aug 26, 2015 at 15:11
  • Thanks. I meant name. It's corrected now. Commented Aug 26, 2015 at 15:14
  • Perfect! Change foreach to for and removed the BeginCollectionItem and it works perfectly. @DanielGpeReyes, if you'll submit that as an answer, I'll gladly accept it and upvote it. Commented Aug 26, 2015 at 15:30
  • Despite the accepted answer, the real problem is that your not understanding EditorTemplate. RatingSectionModel.cshtml should be @model RatingSectionModel (not List<T>). EditorFor() accepts IEnumerable<T>and renders the html based on the template for each item in the collection. Also you don't need to check for null first. Its Just @Html.EditorFor(m => m.RatingSections) (I assume Sections was a typo) Commented Aug 26, 2015 at 23:57
  • Thank you for the information. I had seen that before, but when I tried to code it that way, it threw an exception saying that the model wasn't IEnumerable. I'm sure I had something wrong, but on this particular project I don't have time to go back and explore it. Commented Aug 27, 2015 at 0:01

1 Answer 1

1

You should change your foreach to for, and the binding will work!

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.