8

多くのテンプレート エンジンには、 と を組み合わせた特殊な構文があり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 View Engine を使用すると、テンプレートは次のようになり、コレクション内のアイテム数の追加チェックが必要になります。

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

私の質問は、Razor View Engine を使用して何らかの方法で同様のエレガンスを実現できるかどうかです。

4

4 に答える 4

9

JamiecとMartinブースの回答を統合します。次の拡張メソッドを作成しました。最初の引数としてIEnumerableを取り、次にテキストをレンダリングするための2つのデリゲートを取ります。Razorビューでは、これらの2つのパラメーターをTemplatedDelegatesに渡すことができます。要するに、これはあなたがテンプレートで与えることができることを意味します。したがって、拡張メソッドとその呼び出し方法は次のとおりです。

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

全体として、かなりきれいです。

コメントで行われた提案を実装するUPDATE 。この拡張メソッドは、コレクション内のアイテムをループするために1つの引数を取り、カスタムHelperResultを返します。その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>
        )
)
于 2011-10-19T11:42:47.450 に答える
2

このようなことを達成するために私が考えることができる唯一の方法は、次のいくつかの拡張機能を使用すること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;
    }
}

これにより、次のコードを使用するhttp://rextester.com/runco​​de ?code=AEBQ75190 の実際の例で示されるように、2 つの呼び出しを相互に連鎖させることができます。

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

これがRazorテンプレートにどのように適合するかは、まだわかりません....しかし、おそらくこれで何かを続けることができます.

于 2011-10-19T11:10:30.943 に答える
1

何も構築されていませんが、ニーズに合わせてこれを拡張できます。

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

それでも回答がない場合は、電話を使用していないときに後でお手伝いできるかもしれません

于 2011-10-19T11:15:20.330 に答える
0

質問が提起されたときにはこれは不可能だったかもしれませんが、私はこれを次のように達成しました:

@if (Model.EmailAddress.Count() > 0)
{
    foreach (var emailAddress in Model.EmailAddress)
    {
        <div>@emailAddress.EmailAddress</div>
    }
} else { <span>No email addresses to display</span>  }
于 2018-01-18T15:23:40.137 に答える