I am trying to write a helper in Razor that looks like the following:
@helper DoSomething(Expression> expr) where T : clas
In all cases the TModel
will be the same (the model declared for the view), and in my case, the TValue
was going to be the same, so I was able to declare the Expression argument type:
@helper FormRow(Expression<Func<MyViewModel, MyClass>> expression) {
<div class="form-group">
@(Html.LabelFor(expression, new { @class = "control-label col-sm-6 text-right" }))
<div class="col-sm-6">
@Html.EnumDropDownListFor(expression, new { @class = "form-control" })
</div>
@Html.ValidationMessageFor(expression)
</div>
}
If your model fields are all string
, then you can replace MyClass
with string
.
It might not be bad to define two or three helpers with the TValue
defined, but if you have any more that would generate some ugly code, I didn't really find a good solution. I tried wrapping the @helper
from a function I put inside the @functions {}
block, but I never got it to work down that path.
This is possible to achieve inside a helper file with the @functions
syntax but if you want the razor-style readability you are referring to you will also need to call a regular helper to do the HTML fit and finish.
Note that functions in a Helper file are static so you would still need to pass in the HtmlHelper instance from the page if you were intending to use its methods.
e.g. Views\MyView.cshtml:
@MyHelper.DoSomething(Html, m=>m.Property1)
@MyHelper.DoSomething(Html, m=>m.Property2)
@MyHelper.DoSomething(Html, m=>m.Property3)
App_Code\MyHelper.cshtml:
@using System.Web.Mvc;
@using System.Web.Mvc.Html;
@using System.Linq.Expressions;
@functions
{
public static HelperResult DoSomething<TModel, TItem>(HtmlHelper<TModel> html, Expression<Func<TModel, TItem>> expr)
{
return TheThingToDo(html.LabelFor(expr), html.EditorFor(expr), html.ValidationMessageFor(expr));
}
}
@helper TheThingToDo(MvcHtmlString label, MvcHtmlString textbox, MvcHtmlString validationMessage)
{
<p>
@label
<br />
@textbox
@validationMessage
</p>
}
...
if your main problem is to get name attribute value for binding using lambda expression seems like the @Html.TextBoxFor(x => x.MyPoperty)
, and if your component having very complex html tags and should be implemented on razor helper, then why don't just create an extension method of HtmlHelper<TModel>
to resolve the binding name:
namespace System.Web.Mvc
{
public static class MyHelpers
{
public static string GetNameForBinding<TModel, TProperty>
(this HtmlHelper<TModel> model,
Expression<Func<TModel, TProperty>> property)
{
return ExpressionHelper.GetExpressionText(property);
}
}
}
your razor helper should be like usual:
@helper MyComponent(string name)
{
<input name="@name" type="text"/>
}
then here you can use it
@TheHelper.MyComponent(Html.GetNameForBinding(x => x.MyProperty))
No, this is not currently possible. You could write a normal HTML helper instead.
public static MvcHtmlString DoSomething<T, U>(
this HtmlHelper htmlHelper,
Expression<Func<T, U>> expr
) where T : class
{
...
}
and then:
@(Html.DoSomething<SomeModel, string>(x => x.SomeProperty))
or if you are targeting the model as first generic argument:
public static MvcHtmlString DoSomething<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expr
) where TModel : class
{
...
}
which will allow you to invoke it like this (assuming of course that your view is strongly typed, but that's a safe assumption because all views should be strongly typed anyways :-)):
@Html.DoSomething(x => x.SomeProperty)