许多模板引擎都有一种特殊的语法,它是foreach
和else
的组合。基本上else
子句在foreach
循环没有任何迭代时执行。如果您想在列表后备中显示某种 no项目,这可能很有用。
{% 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以某种方式实现类似的优雅。
答案 0 :(得分:9)
巩固Jamiec和Martin Booth的答案。我创建了以下扩展方法。它将IEnumerable作为第一个参数,然后使用两个委托来呈现文本。在Razor Views中,我们可以传递 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。在那个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>
)
)
答案 1 :(得分: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;
}
}
这使您可以将2个调用相互链接,如本实例所示: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"));
这很适合用Razor模板我仍然不确定....但也许这会让你有所作为。
答案 2 :(得分:1)
没有任何内置的东西,但您可以扩展它以满足您的需求:
http://haacked.com/archive/2011/04/14/a-better-razor-foreach-loop.aspx
如果你还没有答案,我可能会在以后没有使用手机时提供帮助
答案 3 :(得分:0)
在提出这个问题时,也许这是不可能的,但我只是这样做了:
@if (Model.EmailAddress.Count() > 0)
{
foreach (var emailAddress in Model.EmailAddress)
{
<div>@emailAddress.EmailAddress</div>
}
} else { <span>No email addresses to display</span> }