2011-10-19 1 views
8

많은 템플릿 엔진은 foreachelse의 조합 인 특별한 종류의 구문을 사용합니다. 기본적으로 else 절은 foreach 루프에 반복이 없을 때 실행됩니다. 어떤 종류의 을 표시하려는 경우 유용 할 수 있습니다. 대체 목록에 항목이 없습니다.면도기에서 우아한 foreach - else 구조

Twig에서 예를 들어, for loop는 면도기보기 엔진을 사용하여이

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

처럼 보일 수 있습니다, 템플릿은 컬렉션의 항목 수에 추가 검사를 포함,이 같은 싶습니다

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

그럼 내 질문은 : 면도기 뷰 엔진을 사용하여 비슷한 우아함을 얻을 수 있습니까?

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

이것은 당신이 개 통화를 서로이 라이브 예에 의해 demonstarted로에 체인 수 있습니다 : 나는 이런 식으로 뭔가를 달성하기 위해 생각할 수있는

답변

9

Jamiec과 Martin Booth의 답변을 통합했습니다. 나는 다음의 확장 메소드를 생성했다. 첫 번째 인수로 IEnumerable을 사용하고 텍스트를 렌더링하는 데 두 명의 대리자를 사용합니다. 면도기 뷰에서는 템플릿 대표 두 개의 매개 변수를 전달할 수 있습니다. 간단히 말해서 이것은 템플릿을 줄 수 있음을 의미합니다. 그래서 여기에 확장 방법이며 당신이 그것을 호출하는 방법 :

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

그리고 면도기보기의

:

@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> 
     ) 
) 
+0

쉼표는 거의 보이지 않습니다. : P –

+0

나는 동의한다. 그러나 우리가 그것을 우회 할 수있는 실제 방법이 없다. 그렇지 않습니까? :) – Thomas

+1

else 절에 명명 된 매개 변수를 사용하면 더 읽기 쉽고 선택 사항으로 만들 수 있습니다. –

2

유일한 방법은 IEnumerable<T>의 확장 몇 함께 http://rextester.com/runcode?code=AEBQ75190하는 다음 코드를 사용하여이 아직도 확신이 면도칼 템플릿 메신저에 딱 맞는 ....하지만 어쩌면이 당신에게 계속 뭔가를주는 아주 방법

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

.

1

아무것도 AFAIK 내장,하지만 당신은 아마 당신의 필요에 맞게이를 확장 할 수 :

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

난 당신이 아직하지 않으면 내 전화를 사용하지 않는 경우 나중에 도움을 줄 수 있습니다를 대답이 있습니다

+1

나는 이것을 jamiec의 대답과 결합하면 해결책이 생길 것이라고 생각합니다! –

0

어쩌면 이것이 가능하지,하지만 난 그냥 같은이 달성했습니다

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