Estoy tratando de escribir un ayudante en Razor que se parezca a lo siguiente:
@helper DoSomething<T, U>(Expression<Func<T, U>> expr) where T : class
Desafortunadamente, el analizador cree que <T
es el comienzo de un elemento HTML y termino con un error de sintaxis. ¿Es posible crear un ayudante con Razor que sea un método genérico? Si es así, ¿cuál es la sintaxis?
asp.net-mvc
generics
asp.net-mvc-3
razor
mkedobbs
fuente
fuente
static
, a menos que los detalles de implementación lo prohíban; la razón es que uno podría usar ayudantes de extensión genéricos :@helper Foo<T>(this T o) where T : IBar { }
Respuestas:
No, esto no es posible actualmente. En su lugar, podría escribir un ayudante HTML normal.
public static MvcHtmlString DoSomething<T, U>( this HtmlHelper htmlHelper, Expression<Func<T, U>> expr ) where T : class { ... }
y entonces:
@(Html.DoSomething<SomeModel, string>(x => x.SomeProperty))
o si está apuntando al modelo como primer argumento genérico:
public static MvcHtmlString DoSomething<TModel, TProperty>( this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expr ) where TModel : class { ... }
lo que le permitirá invocarlo así (asumiendo, por supuesto, que su vista está fuertemente tipada, pero esa es una suposición segura porque todas las vistas deben estar fuertemente tipadas de todos modos :-)):
fuente
Esto se puede lograr dentro de un archivo de ayuda con la
@functions
sintaxis, pero si desea la legibilidad de estilo navaja a la que se refiere, también deberá llamar a un ayudante habitual para que ajuste y termine el HTML.Tenga en cuenta que las funciones en un archivo Helper son estáticas, por lo que aún deberá pasar la instancia de HtmlHelper desde la página si tiene la intención de usar sus métodos.
por ejemplo, Views \ MyView.cshtml:
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> } ...
fuente
System.Web.WebPages.Html.HtmlHelper
lugar deSystem.Web.Mvc.HtmlHelper
. Existe una excelente posibilidad de que laWebPages
versión no sea adecuada para usted, ya que la mayoría de los métodos de extensión están escritos en contraSystem.Web.Mvc.HtmlHelper
. Además, no existe ningunaUrl
propiedad yUrlHelper
requiere unaRequestContext
que no está disponible en laWebPages
versión. Con todo, probablemente tendrá que pasar elMvc
HtmlHelper
.{MyMvcProject}\App_Code`. It doesn't work as advertised when you place it elsewhere. The error *Cannot access non-static method 'TheThingToDo' in static context* disappears when you move
MyHelper.cshtml` enApp_Code
.DoSomething
debe ser estático, de modo que pueda llamar@MyHelper.DoSomething(..)
en su vista. Si lo convierte en no estático, deberá crear una instancia deMyHelper
first.En todos los casos, el
TModel
será el mismo (el modelo declarado para la vista), y en mi caso, elTValue
iba a ser el mismo, por lo que pude declarar el tipo de argumento Expresión:@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> }
Si los campos de su modelo son todos
string
, puede reemplazarlosMyClass
constring
.Puede que no sea malo definir dos o tres ayudantes con el
TValue
definido, pero si tiene más que generarían un código desagradable, realmente no encontré una buena solución. Intenté envolver el@helper
desde una función que puse dentro del@functions {}
bloque, pero nunca logré que funcionara en ese camino.fuente
TModel
que probablemente lo sepas de antemano.si su principal problema es obtener el valor del atributo de nombre para el enlace mediante la expresión lambda
@Html.TextBoxFor(x => x.MyPoperty)
, y si su componente tiene etiquetas html muy complejas y debe implementarse en el asistente de afeitar, ¿por qué no crear un método de extensiónHtmlHelper<TModel>
para resolver el nombre de enlace: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); } } }
su ayudante de afeitado debe ser como de costumbre:
@helper MyComponent(string name) { <input name="@name" type="text"/> }
entonces aquí puedes usarlo
fuente
@Htm.IdFor
pero necesita un proceso adicional para convertirlo en string (.ToHtmlString()
) donde el asistente requiere string