Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC field id generation, partial views loaded by AJAX create non-unique names

We use the ASP.NET MVC's HTML helpers for generating forms, therefore names for form fields are also generated by those HTML helpers.

Whenever I load a partial view via AJAX into my current page (e.g. into a modal dialog), I run into problems if the model for this partial view contains a field that is named the same as some other field in the original view's model (because ASP.NET MVC generates the same IDs).

Is there any way to tell a partial view to render its fields with a specific prefix (in such a way, that later model binding understands those prefixed names)?

Currently we renamed fields in partial view models to "PartialDateOfBirth" to not interfere with the original page's model "DateOfBirth", but that sucks and doesn't work if you load the same partial view multiple times via AJAX into the page...

Any best practice solution for the problem?

like image 287
D.R. Avatar asked Sep 18 '13 07:09

D.R.


1 Answers

Here's what I would do:

Save this as HtmlPrefixScopeExtensions.cs in your project

public static class HtmlPrefixScopeExtensions
{
    public static IDisposable BeginPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
    {
        return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
    }

    internal class HtmlFieldPrefixScope : IDisposable
    {
        internal readonly TemplateInfo TemplateInfo;
        internal readonly string PreviousHtmlFieldPrefix;

        public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
        {
            TemplateInfo = templateInfo;
            PreviousHtmlFieldPrefix = TemplateInfo.HtmlFieldPrefix;
            TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix;
        }

        public void Dispose()
        {
            TemplateInfo.HtmlFieldPrefix = PreviousHtmlFieldPrefix;
        }
    }
}

Then change your view from for example:

<div class="content">
    <div>
        @Html.EditorFor(model => model.Name)
    </div>
    <div>
        @Html.EditorFor(model => model.Population)
    </div>
</div>

To:

@using (Html.BeginPrefixScope("Country"))
{
    <div class="content">
        <div>
            @Html.EditorFor(model => model.Name)
        </div>
        <div>
            @Html.EditorFor(model => model.Population)
        </div>
    </div>
}

Last but not least, don't forget to either include a using statement in the view matching the location of HtmlPrefixScopeExtensions.cs, for example:

@using YourNamespace.Helpers

or add the correct namespace to Views/Web.config (this is by far the recommended option, you only do it once!):

<namespaces>
    <add namespace="System.Web.Helpers" />
    ...... 
   <add namespace="YourNamespace.Helpers" /> 
</namespaces>

Now: the name of fields will be for example "Country.Name"

You must then have matching name in your post e.g.:

[HttpPost]
public ActionResult SaveCountry(Country country)
{
    // save logic
    return View();
}

Credits: I stripped down Steve Sanderson's wonderful BeginCollectionItem class

like image 199
joargp Avatar answered Nov 11 '22 16:11

joargp