2011-10-19 7 views
8

Muchos motores de plantillas tienen un tipo especial de sintaxis que es una combinación de foreach y else. Básicamente, la cláusula else se ejecuta cuando el ciclo foreach no tiene iteraciones. Esto puede ser útil si desea mostrar algún tipo de sin elementos en la lista de respaldo.Elegante foreach - else construir en Razor

En Twig por ejemplo, el for loop puede tener este aspecto

{% for user in users %} 
    <li>{{ user.username|e }}</li> 
{% else %} 
    <li><em>no user found</em></li> 
{% endfor %} 

Uso de la maquinilla de afeitar Vista del motor, la plantilla le gustaría como esta, que implica una comprobación adicional del número de elementos de la colección:

@foreach (var user in users) { 
    <li>@user.UserName</li> 
} 
@if (!users.Any()) { 
    <li><em>no user found</em></li> 
} 

Así que mi pregunta es: ¿podemos lograr una elegancia similar de una forma u otra utilizando el motor Razor View.

Respuesta

9

Consolidando las respuestas de Jamiec y Martin Booth. Creé el siguiente método de extensión. Se necesita un IEnumerable como primer argumento, y luego dos delegados para representar el texto. En las Vistas Razor podemos pasar Delegados Templados dos de estos parámetros. En resumen, esto significa que puede entregar plantillas. Así que aquí es el método de extensión y cómo se le puede llamar:

public static HelperResult Each<TItem>(this IEnumerable<TItem> items, 
     Func<TItem, HelperResult> eachTemplate, 
     Func<dynamic, HelperResult> other) 
    { 
     return new HelperResult(writer => 
     { 
      foreach (var item in items) 
      { 
       var result = eachTemplate(item); 
       result.WriteTo(writer); 
      } 

      if (!items.Any()) 
      { 
       var otherResult = other(new ExpandoObject()); 
       // var otherResult = other(default(TItem)); 
       otherResult.WriteTo(writer); 
      } 
     }); 
    } 

Y en las vistas Razor:

@Model.Users.Each(
    @<li>@item.Name</li>, 
    @<li> 
     <b>No Items</b> 
    </li> 
) 

Con todo, bastante limpio.

ACTUALIZACIÓN implementando las sugerencias hechas en los comentarios. Este método de extensión toma un argumento para recorrer los elementos en la colección y devuelve un HelperResult personalizado. En ese resultado, uno puede llamar al método Else para pasar un delegado de plantilla en caso de que no se encuentren ítems.

public static class HtmlHelpers 
{ 
    public static ElseHelperResult<TItem> Each<TItem>(this IEnumerable<TItem> items, 
     Func<TItem, HelperResult> eachTemplate) 
    { 
     return ElseHelperResult<TItem>.Create(items, eachTemplate); 
    } 
} 

public class ElseHelperResult<T> : HelperResult 
{ 
    private class Data 
    { 
     public IEnumerable<T> Items { get; set; } 
     public Func<T, HelperResult> EachTemplate { get; set; } 
     public Func<dynamic, HelperResult> ElseTemplate { get; set; } 

     public Data(IEnumerable<T> items, Func<T, HelperResult> eachTemplate) 
     { 
      Items = items; 
      EachTemplate = eachTemplate; 
     } 

     public void Render(TextWriter writer) 
     { 
      foreach (var item in Items) 
      { 
       var result = EachTemplate(item); 
       result.WriteTo(writer); 
      } 

      if (!Items.Any() && ElseTemplate != null) 
      { 
       var otherResult = ElseTemplate(new ExpandoObject()); 
       // var otherResult = other(default(TItem)); 
       otherResult.WriteTo(writer); 
      } 
     } 
    } 

    public ElseHelperResult<T> Else(Func<dynamic, HelperResult> elseTemplate) 
    { 
     RenderingData.ElseTemplate = elseTemplate; 
     return this; 
    } 

    public static ElseHelperResult<T> Create(IEnumerable<T> items, Func<T, HelperResult> eachTemplate) 
    { 
     var data = new Data(items, eachTemplate); 
     return new ElseHelperResult<T>(data); 
    } 

    private ElseHelperResult(Data data) 
     : base(data.Render) 
    { 
     RenderingData = data; 
    } 

    private Data RenderingData { get; set; } 
} 

Esto entonces se puede llamar de la siguiente manera:

@(Model.Users 
    .Each(@<li>@item.Name</li>) 
    .Else(
     @<li> 
      <b>No Users</b> 
     </li> 
     ) 
) 
+0

La coma es casi invisible. : P –

+0

Estoy de acuerdo, pero no hay una manera real en que podamos eludir eso, ¿podemos? :) – Thomas

+1

Si usa un parámetro con nombre para la cláusula else es probable que sea aún más legible y puede hacerlo opcional –

2

La única manera de que pudiera pensar para lograr algo como esto es con un par de extensiones a IEnumerable<T>:

public static class IEnumerableExtensions 
{ 
    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T> action) 
    { 
     foreach(T item in enumerable) 
      action(item); 

     return enumerable; 
    } 

    public static IEnumerable<T> WhenEmpty<T>(this IEnumerable<T> enumerable, Action action) 
    { 
     if(!enumerable.Any()) 
      action(); 
     return enumerable; 
    } 
} 

Esto le permite a la cadena 2 llamadas en sí como demonstarted por este ejemplo vivo: http://rextester.com/runcode?code=AEBQ75190 cuales utilizando el siguiente código:

var listWithItems = new int[] {1,2,3}; 
var emptyList = new int[]{}; 

listWithItems.ForEach(i => Console.WriteLine(i)) 
    .WhenEmpty(() => Console.WriteLine("This should never display")); 

emptyList.ForEach(i => Console.WriteLine(i)) 
    .WhenEmpty(() => Console.WriteLine("This list was empty")); 

Muy bien cómo esto podría encajar con una plantilla im Razor todavía no está seguro de .... pero tal vez esto le da algo para seguir adelante.

1

Nada construido en que yo sepa, pero es probable que podría extender este se adapte a sus necesidades:

http://haacked.com/archive/2011/04/14/a-better-razor-foreach-loop.aspx

que podría ser capaz de ayudar más tarde, cuando no estoy usando mi teléfono si todavía no lo hace tiene una respuesta

+1

¡Creo que si combinas esto con la respuesta de Jamiec tendrás una solución! –

0

Tal vez esto no era posible cuando se planteó la pregunta, pero yo he conseguido esto como esto:

@if (Model.EmailAddress.Count() > 0) 
{ 
    foreach (var emailAddress in Model.EmailAddress) 
    { 
     <div>@emailAddress.EmailAddress</div> 
    } 
} else { <span>No email addresses to display</span> } 
Cuestiones relacionadas