1

This question has been asked but none could solve my problem. The problem is missing or null data from the partial view is not submittied (POST) along with the main view data.

I have a typed partial view called _Address.cshtml that I include in another view called Site.cshtml.

The typed site view binds to a view model called SiteEditModel.cs

public class SiteEditModel
{
   ...properties

   public AddressEditModel Address {get;set;}

  public SiteEditModel()
  {
   Address = new AddressEditModel();
  }
}

The Site view has a form:

        @model Insight.Pos.Web.Models.SiteEditModel
        ...

        @using ( Html.BeginForm( "Edit", "Site", FormMethod.Post ) )
        {
            @Html.HiddenFor( m => m.SiteId )

            ...

            @Html.Partial( "~/Views/Shared/Address.cshtml", this.Model.Address )

            ...

            @Html.SaveChangesButton()
        }

The partial Address view is just a bunch of @Html... calls that bind to the Address model.

@model Insight.Pos.Web.Models.AddressEditModel
@{
    Layout = null;
}
<div>
@Html.HiddenFor(...)
@Html.HiddenFor(...)
@Html.HiddenFor(...)
@hmtl.LabelFor(...)
</div>

In the controller action Edit I can see the SiteEditModel is populated correctly, the Address property of that model is not. Where do I go wrong?

Thank you so much.

John
  • 3,591
  • 8
  • 44
  • 72

5 Answers5

2

http://davybrion.com/blog/2011/01/prefixing-input-elements-of-partial-views-with-asp-net-mvc/

@Html.Partial("~/Views/Shared/_Address.cshtml", Model.Address, new ViewDataDictionary
{
    TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "Address" }
})  
P6345uk
  • 703
  • 10
  • 26
1

The key to fix this is with naming of the partialviews input-elements. The Render partial dont know it's a part of something bigger.

I've make an simple example on how you can fix this in a way that you can have multiple Addresses using the same partial view:

public static class HtmlHelperExtensions
    {
        public static MvcHtmlString PartialWithPrefix(this HtmlHelper html, string partialViewName, object model, string prefix)
        {
            var viewData = new ViewDataDictionary(html.ViewData)
            {
                TemplateInfo = new TemplateInfo
                {
                    HtmlFieldPrefix = prefix
                }
            };

            return html.Partial(partialViewName, model, viewData);
        }
    }

And use this extensions in the view like this:

@using (Html.BeginForm("Edit", "Site", FormMethod.Post))
{
    @Html.HiddenFor(m => m.SiteId)

    @Html.PartialWithPrefix("_Adress", this.Model.Address, "Address")
    <input type="submit" />
}

You can of course make this a bit more fancy with expressions and reflection but that's another question ;-)

Daniel Stackenland
  • 3,149
  • 1
  • 19
  • 22
0

Your SiteEditModel Address property is not marked as public, change it to this instead:

public AddressEditModel Address {get;set;}

I would also change your partial to use SiteEditModel instead:

@model Insight.Pos.Web.Models.SiteEditModel
@{
    Layout = null;
}
<div>
@Html.HiddenFor(m => m.Address.FooProperty)

...

</div>

This would mean that your properties would end up being named correctly in order for the model binder to pick them up. Using the above example it would be Name"=Address.FooProperty".

hutchonoid
  • 32,982
  • 15
  • 99
  • 104
  • Thanks. The Address property is already public. That wasn't it. I corrected that in my question. As for using the SiteEditModel in the partial view-- doesn't this beat the goal of having a partial view there? There are other "main" views that also use the _Address partial view, so I suppose I can't pass in the "main view" EditModel? – John Sep 03 '15 at 11:19
  • @John Your partial will be the same, just the `model` reference will change to enable the model binder naming. – hutchonoid Sep 03 '15 at 12:04
0

As I remember correctly, the problem is that Html.Partial doesn't populate inputs names correctly. You should have something like:

<input id="Address.Street" name="Address.Street" />

but I assume you have following HTML:

<input id="Street" name="Street" />

You have few solutions:

  1. Insert input name manually:

     @Html.HiddenFor(x => x.Street, new { Name = "Address.Street" })
    
  2. Use Html.EditorFor()

  3. Override names resolving in Html.Partial()

The downside of first solution is that you are hardcoding property name, what isn't ideal. I'd recommend using Html.EditorFor() or Html.DisplayFor() helpers, cause they populate inputs names correctly.

kamil-mrzyglod
  • 4,948
  • 1
  • 20
  • 29
0

Model binder could not bind child models correctly if you are populating them in partial view. Consider using editor templates instead which is implemented for this reason.

Put your AddressEditModel.cshtml file in \Views\Shared\EditorTemplates\ folder and in your main view use like this:

@using ( Html.BeginForm( "Edit", "Site", FormMethod.Post ) )
{
    // because model type name same as template name MVC automatically picks our template
    @Html.EditorFor(model=>model.Address)

    // or if names do not match set template name explicitly 
    @Html.EditorFor(model=>model.Address,"NameOfTemplate")
}     
Sam FarajpourGhamari
  • 14,601
  • 4
  • 52
  • 56