MVC Submit Form With Partial View

10,331

Yes it's possible.

The issue you are facing is due to how model binding work. If it was just a primitive type you are trying to bind for the IEnumerable, then it would have worked fine.

@foreach(var item in @Model.XmlSchema.MappedFields)
    {
        string itemValue = Model.XmlFields.FirstOrDefault(x => x.XmlElement == item.XmlElement).XmlValue;
        <div style="width: 300px; display: inline-block;">
            @Html.DisplayFor(i => item.XmlElement)
        </div>
        <div style="width: 300px; display: inline-block;">
            @itemValue
        </div>
        <div class="busoblist" style="display: inline-block;">
            @Html.DropDownListFor(i => item.BusinessObjectField, new SelectList(Model.BusinessObjectFields))
        </div>
        <hr />
    }

if you take a look at the source code generated by the above code, you will see all of the dropdowns (select tags) generated from the above code has the same name. because of this, it will not bind to your model properly when you submit the form.

Following is a good blog post which I found very useful and it explains about this in detail

http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/

UPDATE

For those of you who come here looking for an answer for posting data to a list, I've written a blog post. http://amilaudayanga.wordpress.com/2014/03/05/posting-data-to-a-list-in-asp-net-mvc-part1/

Share:
10,331
Mark
Author by

Mark

Updated on June 29, 2022

Comments

  • Mark
    Mark almost 2 years

    Apologies if this has been answered before, I can't find anything that matches my issue.

    I have a View, which contains a Partial View, and when submitting the View, the data from the partial view is not sent to the controller. I understand that if I use an editor template, this should work, but I'm unable to use this, as I need data from my main model inside the Partial.

    My Create View (simplified):

    @model CreateXmlSchemaModel
    @using (Html.BeginForm("Create", "XmlSchema", FormMethod.Post, new { enctype = "multipart/form-data" }))
    {
        <fieldset>
            <div class="editor-label">
                @Html.LabelFor(m => m.XmlSchema.Name)
            </div>
            <div class="editor-field">
                @Html.EditorFor(m => m.XmlSchema.Name)
            </div>
            <div id="fieldmapping">
                @Html.Partial("_XmlFieldMapping")
            </div>
            <div>
                <input type="submit" value="Create" class="button" />
            </div>
        </fieldset>
    }
    

    My Model structure (simplified):

    public abstract class XmlSchemaModelContainerBase
    {
        public IEnumerable<string> BusinessObjects { get; set; }
        public IEnumerable<XmlInputFieldModel> XmlFields { get; set; }
        public IEnumerable<string> BusinessObjectFields { get; set; }
    }
    
    public class XmlSchemaModelContainer : XmlSchemaModelContainerBase
    {
        public XmlSchemaModel XmlSchema { get; set; }
    }
    
    public class XmlSchemaModel
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public string BusinessObject { get; set; }
        public IEnumerable<XmlSchemaField> MappedFields { get; set; }
    }
    
    public class XmlSchemaField
    {
        public string XmlElement { get; set; }
        public string BusinessObjectField { get; set; }
    }
    
    public class XmlInputFieldModel
    {
        public string XmlElement { get; set; }
        public string XmlValue { get; set; }
    }
    
    public class CreateXmlSchemaModel : XmlSchemaModelContainer
    {
        [Required(ErrorMessage = "File upload required")]
        public HttpPostedFileBase Xml { get; set; }
    }
    

    My Controller (I won't post it all, as I can tell immediately that my viewModel does not contains the MappedFields):

    [HttpPost]
    public ActionResult Create(CreateXmlSchemaModel viewModel)
    {
    }
    

    My _XmlMappedFields Partial View:

    @model CherwellXmlConnector.Models.XmlSchemaModelContainer
    @{
        Model.XmlSchema.MappedFields = Model.XmlSchema.MappedFields.OrderBy(i => i.XmlElement);
    }
    
    <div>
        <div>
            @for(int i = 0; i < @Model.XmlFields.Count(); i ++)
            {
                List<CherwellXmlConnector.Models.XmlSchemaField> fields = Model.XmlSchema.MappedFields.ToList();
                string itemValue = Model.XmlFields.FirstOrDefault(x => x.XmlElement == fields[i].XmlElement).XmlValue;
    
                <div style="width: 300px; display: inline-block;">
                    @Html.DisplayFor(x => fields[i].XmlElement)
                </div>
                <div style="width: 300px; display: inline-block;">
                    @itemValue
                </div>
                <div class="busoblist" style="display: inline-block;">
                    @Html.DropDownListFor(x => fields[i].BusinessObjectField, new SelectList(Model.BusinessObjectFields))
                </div>
                <hr />
            }
        </div>
    </div>
    

    My intention is to display to the user in the partial, a list of all the given XmlFields and their values, and allow them to select a BusinessObjectField from a dropdown for each XmlField. I then want this submitted back to the Controller inside the IEnumerable<XmlSchemaField> MappedFields object. Is this possible?

  • Nilzor
    Nilzor about 10 years
    Just to add to that: Change the foreach loop to a for-loop and use old-school indexing instead, and you'll be good to go.
  • Mark
    Mark about 10 years
    @Amila Thanks for this, and I've followed the article through, but I must be missing some final bind or something, as it's still not coming across to the controller. I've updated my post above with the changes I made.
  • Amila
    Amila about 10 years
    @Html.Partial("_XmlFieldMapping") you are not passing the model to the partial. you should pass the object which is of type CherwellXmlConnector.Models.XmlSchemaModelContainer
  • Mark
    Mark about 10 years
    Oh, but I am. From the Create view, a dropdown is changed, which fires a get.JSON event. The controller creates a new instance of XmlSchemaModelContainer, and renders this, sending it back to the Create View. I then set the html of the #mappedfields partial to my rendered view.
  • Amila
    Amila about 10 years
    can you please update your post with the [HttpGet] Create action and how you create the XmlSchemaModelContainer instance.By the way, does the updated for loop generate any html at the moment?
  • Mark
    Mark about 10 years
    @Amila. I've managed to get it working just now. I was being stupid. In the changes I made to the Partial, I was instantiating a new List of MappedFields, because I couldn't apply the index to an IEnumerable. I was then using this new list to manipulate the fields, but wasn't sending this back. I've changed my XmLSchemaModel to have an Array of MappedFields now, and now directly reference this in the partial for loop. Thanks for your time and patience :)
  • Amila
    Amila about 10 years
    No worries, I am glad I was able to help. This is a good topic to blog about if and when I have time.