ASP.NET MVC partial views: input name prefixes

Viewed 55893

Suppose I have ViewModel like

public class AnotherViewModel
{
   public string Name { get; set; }
}
public class MyViewModel
{
   public string Name { get; set; }
   public AnotherViewModel Child { get; set; }
   public AnotherViewModel Child2 { get; set; }
}

In the view I can render a partial with

<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>

In the partial I'll do

<%= Html.TextBox("Name", Model.Name) %>
or
<%= Html.TextBoxFor(x => x.Name) %>

However, the problem is that both will render name="Name" while I need to have name="Child.Name" in order for model binder to work properly. Or, name="Child2.Name" when I render the second property using the same partial view.

How do I make my partial view automatically recognize the required prefix? I can pass it as a parameter but this is too inconvenient. This is even worse when I want for example to render it recursively. Is there a way to render partial views with a prefix, or, even better, with automatic reconition of the calling lambda expression so that

<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>

will automatically add correct "Child." prefix to the generated name/id strings?

I can accept any solution, including 3-rd party view engines and libraries - I actually use Spark View Engine (I "solve" the problem using its macros) and MvcContrib, but did not find a solution there. XForms, InputBuilder, MVC v2 - any tool/insight that provide this functionality will be great.

Currently I think about coding this myself but it seems like a waste of time, I can't believe this trivial stuff is not implemented already.

A lot of manual solutions may exists, and all of them are welcome. For example, I can force my partials to be based off IPartialViewModel<T> { public string Prefix; T Model; }. But I'd rather prefer some existing/approved solution.

UPDATE: there's a similar question with no answer here.

11 Answers

PartailFor for asp.net Core 2 in case someone needs it.

    public static ModelExplorer GetModelExplorer<TModel, TResult>(this IHtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TResult>> expression)
    {
        if (expression == null)
            throw new ArgumentNullException(nameof(expression));
        return ExpressionMetadataProvider.FromLambdaExpression(expression, htmlHelper.ViewData, htmlHelper.MetadataProvider);
    }

    public static IHtmlContent PartialFor<TModel, TResult>(this IHtmlHelper<TModel> helper, Expression<Func<TModel, TResult>> expression, string partialViewName, string prefix = "")
    {
        var modelExplorer = helper.GetModelExplorer(expression);
        var viewData = new ViewDataDictionary(helper.ViewData);
        viewData.TemplateInfo.HtmlFieldPrefix += prefix;
        return helper.Partial(partialViewName, modelExplorer.Model, viewData);
    }

As stated here: https://stackoverflow.com/a/58943378/3901618 - for ASP.NET Core - you can use the partial tag helper.

<partial name="AnotherViewModelControl" for="Child" />
<partial name="AnotherViewModelControl" for="Child2" />

It generates all required name prefixes.

Related