Dot-Net

優雅的 foreach - else 在 Razor 中構造

  • January 18, 2018

許多模板引擎都有一種特殊的語法,它是foreach和的組合else。基本上,當循環沒有任何迭代else時,就會執行該子句。如果您想在列表備份foreach中顯示某種無項目,這可能很有用。

例如,在Twig中,for循環看起來像這樣

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

使用 Razor 視圖引擎,模板會像這樣,涉及對集合中項目數量的額外檢查:

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

所以我的問題是:我們能否使用 Razor 視圖引擎以某種方式實現類似的優雅。

鞏固 Jamiec 和 Martin Booth 的答案。我創建了以下擴展方法。它需要一個 IEnumerable 作為第一個參數,然後是兩個用於呈現文本的委託。在 Razor 視圖中,我們可以在Templated Delegates中傳入兩個這樣的參數。簡而言之,這意味著您可以提供模板。所以這是擴展方法以及如何呼叫它:

   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);
           }
       });
   }

在 Razor 視圖中:

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

總而言之,很乾淨。

更新實施評論中提出的建議。此擴展方法採用一個參數來遍歷集合中的項目並返回自定義 HelperResult。在該幫助結果上,可以呼叫該Else方法以傳入模板委託,以防找不到任何項目。

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; }
}

然後可以按如下方式呼叫:

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

引用自:https://stackoverflow.com/questions/7820117