2011-01-21 13 views
81

Estoy tratando de escribir un ayudante en la maquinilla de afeitar que tiene el siguiente aspecto:¿Es posible crear un método @helper genérico con Razor?

@helper DoSomething<T, U>(Expression<Func<T, U>> expr) where T : class 

Por desgracia, el analizador piensa 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?

+0

Aún no se ha corregido en la versión MVC 4 actual. :( –

+1

¿Cómo todavía no se soluciona esto en VS2012? –

+7

Bondad, no puedo esperar a que se agregue esto, espero que esté en algún lugar alrededor de "* implementarlo ayer *" en la lista de prioridades. Parcialmente fuera del tema, pero junto con esto, me gustaría ver que las clases generadas son 'estáticas', a menos que los detalles de implementación lo prohíban; por lo tanto, se podría usar * ayudantes de extensión genéricos *:' @helper Foo (este T o) donde T: IBar {} ' – Dan

Respuesta

46

No, esto no es posible. En su lugar, podría escribir una ayuda HTML normal.

public static MvcHtmlString DoSomething<T, U>(
    this HtmlHelper htmlHelper, 
    Expression<Func<T, U>> expr 
) where T : class 
{ 
    ... 
} 

y luego:

@(Html.DoSomething<SomeModel, string>(x => x.SomeProperty)) 

o si se dirigen a la modelo como primer argumento genérico:

public static MvcHtmlString DoSomething<TModel, TProperty>(
    this HtmlHelper<TModel> htmlHelper, 
    Expression<Func<TModel, TProperty>> expr 
) where T : class 
{ 
    ... 
} 

que le permitirá a lo invoca como esto (asumiendo por supuesto que su vista está fuertemente tipada, pero eso es una suposición segura porque todas las vistas deben ser fuertemente tipadas de todos modos :-)):

@Html.DoSomething(x => x.SomeProperty) 
+10

Esperemos que esto sea algo que agreguen a una versión futura de los ayudantes Razor. La legibilidad de un ayudante tradicional es mucho más baja que la sintaxis de @helper. – mkedobbs

+2

Sí, estoy de acuerdo. Volver al método anterior no solo apesta, sino que divide tu ayuda de manera arbitraria! –

118

Esta es posible lograr dentro de un archivo de ayuda con la sintaxis @functions pero si quieres la legibilidad de estilo de afeitar que usted se refiere también tendrá que llamar a un ayudante regular para hacer el ajuste y el acabado HTML.

Tenga en cuenta que las funciones en un archivo Helper son estáticas por lo que aún tendría que pasar la instancia de HtmlHelper desde la página si tenía la intención de utilizar sus métodos.

p. Ej. Vistas \ 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> 
} 
... 
+0

Esto es perfecto. Gracias. –

+0

NO es necesario que el método sea estático, y por lo tanto, tampoco es necesario pasar su Html/Url/Model, etc. – Sheepy

+0

Hmmm ¿por qué esto no funciona para mí? Obtengo un "No se puede acceder al método no estático 'TheThingToDo' en contexto estático" .. – TweeZz

0

si su principal problema es conseguir valor de nombre de atributo para la unión mediante expresión lambda parece que el @Html.TextBoxFor(x => x.MyPoperty), y Si su componente tiene etiquetas html muy complejas y debe implementarse en el asistente de afeitado, entonces ¿por qué no simplemente crea un método de extensión de HtmlHelper<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 afeitar debe ser como de costumbre:

@helper MyComponent(string name) 
{ 
    <input name="@name" type="text"/> 
} 

entonces aquí puede utilizarlo

@TheHelper.MyComponent(Html.GetNameForBinding(x => x.MyProperty)) 
+0

¿No es esto para lo que @ Html.IdFor (...) es? –

+0

Sí, puede hacerlo con '@ Htm.IdFor' pero necesitan proceso extra para convertirlo en cadena (' .ToHtmlString() '), donde el ayudante requieren cadena de – ktutnik

2

En todos los casos el TModel será el mismo (el modelo declarado para la vista), y en mi caso, el TValue iba a ser el mismo, así 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, entonces puede reemplazar MyClass con string.

Podría no ser malo definir dos o tres ayudantes con el TValue definido, pero si tiene más que generaría un código feo, realmente no encontré una buena solución. Traté de envolver el @helper desde una función que puse dentro del bloque @functions {}, pero nunca lo hice funcionar en esa ruta.

+0

bastante obvio cuando se piensa en ello - si se trata de' TModel' probablemente lo sabes de antemano. –

Cuestiones relacionadas