Pregunta

Muchos motores de plantilla tienen un tipo especial de sintaxis que es una combinación de foreach y else. Básicamente el else La cláusula se ejecuta cuando el foreach Loop no tiene iteraciones. Esto puede ser útil si desea mostrar algún tipo de No hay elementos en la lista retroceder.

En Ramita Por ejemplo, el for círculo puede verse así

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

Usando el motor Razor View, la plantilla le gustaría así, que implica una verificación adicional de la cantidad de elementos en la colección:

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

Entonces, mis preguntas son: ¿podemos lograr una elegancia similar de una forma u otra usando el motor de vista de afeitar?

¿Fue útil?

Solución

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 por representar el texto. En las vistas de afeitar podemos pasar Delegados plantados dos estos parámetros. En resumen, esto significa que puedes ceder plantillas. Así que aquí está el método de extensión y cómo puede llamarlo:

    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 de afeitar:

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

En general, bastante limpio.

ACTUALIZAR Implementación de las sugerencias hechas en los comentarios. Este método de extensión toma un argumento para recurrir a los elementos en la colección y devuelve un HelperResult personalizado. En ese ayudante, uno puede llamar al Else Método para pasar en una plantilla delegada en caso de que no se encuentren elementos.

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 se puede llamar como sigue:

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

Otros consejos

La única forma en que podría pensar en lograr algo como esto es con un par de extensiones para 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 encadenar 2 llamadas entre sí como demonios de este ejemplo en vivo: http://rextester.com/runcode?code=AEBQ75190 que usa 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"));

Cómo encajaría esto con una plantilla de navaja de afeitar que todavía estoy inseguro ... pero tal vez esto te da algo para continuar.

Nada construido en Afaik, pero probablemente podría extender esto para satisfacer sus necesidades:

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

Es posible que pueda ayudar más tarde cuando no estoy usando mi teléfono si aún no tienes una respuesta

Tal vez esto no fue posible cuando se planteó la pregunta, pero acabo de lograr esto así:

@if (Model.EmailAddress.Count() > 0)
{
    foreach (var emailAddress in Model.EmailAddress)
    {
        <div>@emailAddress.EmailAddress</div>
    }
} else { <span>No email addresses to display</span>  }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top