1

I'm having some issue getting my partial view BeginCollectionItem to save to the database. I have a form which has a dynamic number of "sections" that can be added to the page, and within each of these fields there is a text box where the user can enter the section name.

As far as I can tell the BeginCollectionItem within the partial view is working properly, however I cannot post the info to the database. In my other forms I have used a [bind()] to send the data to the database, is it possible to get this into a list and then post that via a bind?

I've included my code below: The Model:

namespace project.Models.SetupViewModels
{
    public class SOPTopTemplateBuilderViewModel
    {
        public List<Section> Section { get; set; }
    }

    public class Section {
        public int SectionId { get; set; }
        public string SectionText { get; set; }
        public string TopTempId { get; set; }
    }
}

cshtml:

    @model IEnumerable<project.Models.SetupViewModels.Section>
    @using (Html.BeginForm("SOPTopTemplateBuilder", "Setup", FormMethod.Post))
    {
     <div class="main-holder" id="theform">
     @foreach(var item in Model)
     {
         @Html.Partial("_SectionCreator", item)
     }
     </div>
     <button id="add" type="button">Add</button>
         <div class="form-group submit-row">
              <div class="col-12 float-to-right">
                   <input type="submit" class="btn btn-default" value="continue" />
              </div>
         </div>
    }
@section Scripts {
    <script>
$(document).ready(function () {
        var url = '@Url.Action("AddSection")';
        var form = $('form');
        var recipients = $('#theform');
        $('#add').click(function() {
            $.post(url, function(response) {
                recipients.append(response);
                // Reparse the validator for client side validation
                form.data('validator', null);
                $.validator.unobtrusive.parse(form);
                });
            });
        });
    </script>
}

Partial View:

@model project.Models.SetupViewModels.Section
@using HtmlHelpers.BeginCollectionItemCore

@using (Html.BeginCollectionItem("Section"))
{
    <div class="new-section">
        <div>
            <p>New Section</p>
             @Html.HiddenFor(m => m.SectionId, new { @class="id" })
            @Html.EditorFor(m => m.SectionText, new { @class = "form-control limit-form"})
         </div>
    </div>
}

Controller:

[HttpPost]
public PartialViewResult AddSection()
{      
      return PartialView("_SectionCreator", new Section());
}

[HttpGet]
public ActionResult SOPTopTemplateBuilder(){

      List<Section> model = new List<Section>();

      return View(model);
}

[HttpPost]
public ActionResult SOPTopTemplateBuilder(IEnumerable<Section> soptop)
{
      if (ModelState.IsValid)
      {}
      return View(soptop);
}
Lucy Foster
  • 23
  • 1
  • 6
  • Welcome to SO. Can you please rephrase the title to something more natural (summarize your issue in a short sentence). The title is the first thing people sees and may better attract the attention of the right people who may provide an answer. Also please note, there is no "MVC 6". Just the old ASP.NET MVC 5 and the new ASP.NET Core MVC (1.x and 2.x) – Tseng Feb 06 '18 at 16:59

1 Answers1

1

Your use of Html.BeginCollectionItem("Section") perpends Section[xxxx] to the name attribute (where xxxx is a Guid) so that you generating inputs with

<input name="Section[xxxx].SectionId" .... />

which posts back to a model containing a collection property named Sections.

Since you already have a model with that property, you can change the POST method to

[HttpPost]
public ActionResult SOPTopTemplateBuilder(SOPTopTemplateBuilderViewModel soptop)

other options include

  1. Using your existing POST method and omitting the "Section" prefix using Html.BeginCollectionItem("") which will generate name="[xxxx].SectionId"
  2. Changing the POST method signature to public ActionResult SOPTopTemplateBuilder(IEnumerable<Section> section) (where the name of the parameter matches the name of the prefix)
  3. Using a BindAttribute to 'strip' the prefix from the form values public ActionResult SOPTopTemplateBuilder([Bind(Prefix = "Section")]IEnumerable<Section> soptop)

As a side note, your editing data, so you should always use a view model (say public class SectionViewModel) rather than using data models in your view. - What is ViewModel in MVC?

  • Thank you! I've used the binding prefix method & it's worked! I did have to change it to Section rather than Sections, but it's now putting all the info into the database. – Lucy Foster Feb 07 '18 at 12:00