mxmissile
mxmissile

Reputation: 11673

Asp.Net Razor View Passing Expression To Partial

I find myself writing this a whole lot in my views:

<div class="form-group">
    @Html.LabelFor(x => x.City)
    @Html.EditorFor(x => x.City)
    @Html.ValidationMessageFor(x => x.City)
</div>

I'd really like to put this in a Partial _Field.cshtml, something like this:

@model //what model type???

<div class="form-group">
    @Html.LabelFor(Model)
    @Html.EditorFor(Model)
    @Html.ValidationMessageFor(Model)
</div>

That could then be called by:

@Html.Partial("_Field", x => x.City)

What would the @model type in my partial be if I wanted to accomplish something like this?

UPDATE This works, but I'd rather use a partial for ease of changing the template:

public static MvcHtmlString Field<TModel, TItem>(this HtmlHelper<TModel> html, Expression<Func<TModel, TItem>> expr)
{
  var h = "<div class='form-group'>";
  h += $"{html.LabelFor(expr)}{html.EditorFor(expr)}{html.ValidationMessageFor(expr)}";
  h += "</div>";

  return MvcHtmlString.Create(h);
}

Upvotes: 3

Views: 332

Answers (1)

Chris Pratt
Chris Pratt

Reputation: 239290

That's not possible. However, what you want is very similar to editor templates. Essentially, you just create a view in Views/Shared/EditorTemplates named after one of the following conventions:

  • A system or custom type (String.cshtml, Int32.cshtml, MyAwesomeClass.cshtml, etc.)
  • One of the members of the DataType enum (EmailAddress.cshtml, Html.cshtml, PhoneNumber.cshtml, etc.). You would then apply the appropriate DataType attributes to your properties:

    [DataType(DataType.EmailAdress)]
    public string Email { get; set; }
    
  • Any thing you want, in conjunction with the UIHint attribute:

    [UIHint("Foo")]
    public string Foo { get; set; }
    

    Which would then correspond to a Foo.cshtml editor template

In your views, then, you simply use Html.EditorFor:

@Html.EditorFor(x => x.City)

Then, for example, you could have Views/Shared/EditorTemplates/String.cshtml as:

<div class="form-group">
    @Html.Label("", new { @class = "control-label" })
    @Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { @class = "form-control" })
    @Html.ValidationMessage("")
</div>

(The empty quotes are placeholders. Razor will automatically fill in the appropriate property name, thankfully.)

Calling EditorFor, then, will print all of this, rather than just the default text input. You can take this much further, as well. I have some articles on my blog that goes into greater detail, if you're interested.

UPDATE

It's worth mentioning a few features of EditorFor:

  1. You can pass a template directly to the call, meaning you can customize what template is used on the fly and per instance:

    @Html.EditorFor(x => x.City, "MyCustomEditorTemplate")
    
  2. You can pass additionalViewData. The members of this anonymous object are added to the ViewData dynamic dictionary. Potentially, you could use this to branch within your editor template to cover additional scenarios. For example:

    @Html.EditorFor(x => x.City, new { formGroup = false })
    

    Then in your editor template:

    @{ var formGroup = ViewData["formGroup"] as bool? ?? true; }
    @if (formGroup)
    {
        <!-- Bootstrap form group -->
    }
    else
    {
        <!-- Just the input -->
    }
    

Upvotes: 3

Related Questions