tl;dr
到目前为止给出的答案(由@wbarksdale https://stackoverflow.com/users/546427/wbarksdale, @PlexQ https://stackoverflow.com/users/477399/plexq and @丹尼尔·C·索布拉尔 https://stackoverflow.com/users/53013/daniel-c-sobral在评论中)足以解决此处描述的问题。
但他们缺少关于为什么初始代码使用的真正解释foreach
,不起作用。
它无法工作,因为foreach
回报Unit
.
游戏理念
让我快速说明/回忆一下模板的工作原理。
Play Framework 2 默认提供的 Scala 模板系统确实是基于 FP 概念构建的,因此它使用了大量不可变的结构等等。
此外,这样的 Scala 模板(比方说myTemplate.scala.html
) 将被编译成常规的 Scalaobject
其中有一个apply
方法调用。后一个功能使我们能够call该对象作为带有一些参数的函数(在模板的第一行中声明的参数)。
This object
也依赖于像这样的构造BaseScalaTemplate https://github.com/playframework/Play20/blob/master/framework/src/templates/src/main/scala/play/api/templates/ScalaTemplate.scala它是用输出格式化程序(Html)构建的。这个格式化程序将能够接受一些东西(比如String
, Unit
, Seq[Int]
, Map[A,B]
, ...) 并将其渲染为 HTML 代码。
使用时将进行格式化_display_
的方法BaseScalaTemplate
,它返回一个实例格式化输出。该显示方法将在compiled的代码.scala.html
文件中的对象apply
方法的主体。
所以身体可以这样结束:
def apply/*1.2*/(testMap:scala.collection.immutable.Map[String, Int]):play.api.templates.Html =
_display_ {
Seq[Any](
_display_(
Seq[Any](
/*3.2*/testMap/*3.9*/.map/*3.13*/ { e =>
_display_(Seq[Any](_display_(Seq[Any](/*5.3*/e))))
}
)
)
)
}
看?这_display_
调用不会改变任何内容,但它们的组合方式使得 apply 本身将返回格式化代码的实例(Html
)!
这给了我们线索...
是啊等等……现在为什么?
在给出了有关 Play 内部结构的闪电之后,我们现在可以解决真正的问题:为什么问题帖子中提供的意识形态 Scala 代码不起作用......阅读,根本不输出任何内容。
很简单,使用时foreach
on a Map
,你确实是looping超过的项目和adapting将它们转为 Html。但是这些计算将无法被模板系统使用,因为它们被包含在foreach
的循环。那是foreach
当序列中的每个项目都需要副作用时,必须使用...并返回Unit
当它完成时。
因为,模板系统将尝试_display_
的结果foreach
在给定的Map
它只会渲染/格式化Unit
因此一个空的String
!
总而言之,只需使用map
这将返回一个新序列,其中包含adapted项、Html
实例。
嗯,那么呢for
?
是的,你是对的......根据所说的,为什么建议使用的答案是for
循环工作,因为没有产生值,afor
相当于foreach
!? (介绍yield
将结束于map
-类似行为)
答案就在代码中......模板编译器将在前面添加yield
关键字到for
的身体——看看这个here https://github.com/playframework/Play20/blob/master/framework/src/templates-compiler/src/main/scala/play/templates/ScalaTemplateCompiler.scala#L383. :-D
Et voilà,它也有效,因为生成的东西在for
完成后,其主体将附加到返回的序列。