好的,我已经为这个问题挣扎了一段时间,并且花了很多时间尝试不同的事情来做一些我用PHP很容易做到的事情。
我试图迭代一个列表,同时保持跟踪本地变量,同时吐出HTML试图填充一个表。
尝试# 1:
@{
var curDate : Date = null
for(ind <- indicators){
if(curDate == null || !curDate.equals(ind.getFirstFound())){
curDate = ind.getFirstFound()
<tr><th colspan='5' class='day'>@(ind.getFirstFound())</th></tr>
<tr><th>Document ID</th><th>Value</th><th>Owner</th><th>Document Title / Comment</th></tr>
}
}
}
我尝试使用scala块语句来允许我将curDate作为创建范围内的变量。这个块正确地维护了curDate状态,但不允许我向DOM输出任何内容。由于我的未转义的、随机抛出的HTML,我实际上并没有期望它能够编译,但它确实编译了。尽管在服务器上正确地执行了决策结构,但该循环只是在DOM上没有放置任何内容。
我尝试用@Html('...')
转义,但是产生编译错误。
尝试# 2:
大量的谷歌搜索让我找到了"for understanding":
@for(ind <- indicators; curDate = ind.getFirstFound()){
@if(curDate == null || !curDate.equals(ind.getFirstFound())){
@(curDate = ind.getFirstFound())
}
<tr><th colspan='5' class='day'>@(ind.getFirstFound())</th></tr>
<tr><th>Document ID</th><th>Value</th><th>Owner</th><th>Document Title / Comment</th></tr>
}
在这个块中没有if语句,这是我最接近我实际想要做的事情,但显然我不允许重新分配非引用类型,这就是为什么我希望尝试#1的curDate : Date = null
引用声明能够工作。这个尝试获得了页面上的HTML(再次,如果我删除嵌套的if语句),但没有获得
我的问题是,我如何实现这个意图?我非常痛苦地意识到自己缺乏Scala知识,Play模板语法加剧了这一点。我不知道该怎么办。
提前感谢!
Play的模板语言非常倾向于函数式编程。使用可变状态可能实现您想要实现的目标,但您可能最好顺其自然,并使用功能解决方案。
如果你想在函数式编程的循环迭代之间保持状态,那可以通过做一个折叠来完成——你从某个状态开始,在每次迭代中,你得到前一个状态和下一个元素,然后你返回基于这两个元素的新状态。
那么,看看你的第一个解决方案,看起来你要做的只是打印一个元素,如果它的日期与前一个不同,这是正确的吗?另一种表达方式是,你想过滤掉所有与前一个日期相同的元素。用折叠的方式来表达,我们将把这些元素折叠成一个序列(我们的初始状态),如果折叠序列的最后一个元素与当前一个元素的日期不同,我们就添加它,否则我们忽略它。
我们的折叠是这样的:
indicators.foldLeft(Vector.empty[Indicator]) { (collected, next) =>
if (collected.lastOption.forall(_.getFirstFound != next.getFirstFound)) {
collected :+ next
} else {
collected
}
}
只是为了解释上面的内容,我们将折叠成Vector
,因为Vector
具有常数时间附加和最后,List
具有n时间。如果collected
中没有最后一个元素,forall
将返回true,否则,如果有,如果传入lambda的结果为true,它将返回true。在Scala中,==
调用.equals
(在做空检查之后),所以你不需要在Scala中使用.equals
。
那么,把它放到模板中:
@for(ind <- indicators.foldLeft(Vector.empty[Indicator]) { (collected, next) =>
if (collected.lastOption.forall(_.getFirstFound != next.getFirstFound)) {
collected :+ next
} else {
collected
}
}){
...
}