我有这样的模板结构:
modal.scala.view
@()
... HTML code to display a modal in my app ...
foo.scala.view
@()
@scripts {
... The scripts required by foo.scala.view components ...
... The scripts required by modal.scala.view ... I want to avoid this!
}
@main(scripts){
... HTML code of foo ...
@modal
}
main.scala.view
@(scripts: Html)
... Main HTML code ...
@scripts
我想在modal.scala.view中保留modal的脚本,但是我找不到将脚本模板中的脚本传递给父模板的方法,以便将它们呈现在主模板的正确位置。有任何想法吗?提前谢谢!
答案 0 :(得分:1)
我不认为Play团队已经祝福你的问题有一个规范的答案,但我可以想到几种方法:monadic方法和命令式方法。
在子控制器中包裹视图;在输出中封装脚本
我正在开展的一个大项目使用这种策略。我们创建了一个MultipartHtml
类型,其中包含应包含在文档正文中的Html
输出以及我们创建的名为Resources
的类型,其中包含应该转到别处的内容。我们将此类型视为monad,因此我们可以map
和flatMap
操纵Html
文档内容,同时累积和重复删除Resources
。
我们所有的控制器都返回MultipartHtml
。他们根据视图的结果构建一个实例,然后只需:+
Resource
个标记到结果中。我们的页面级控制器将这些部分组合在一起。我们所做的核心看起来像这样:
/**
* Output type for components to render body, head and end-of-body content
* @param bodyMarkup the visual part of the component output
* @param resources tags for content to include in the head or footer
*/
case class MultipartHtml(bodyMarkup: Html, resources: Seq[MultipartHtml.Resource] = Nil) {
import com.huffpost.hyperion.lib.MultipartHtml._
/**
* Apply a transformation to the body content of this object
* @param bodyMapper transformation function
* @return a new object with transformed body content
*/
def map(bodyMapper: Html => Html): MultipartHtml = MultipartHtml(bodyMapper(bodyMarkup), resources)
/**
* @param bodyMapper transformation function
* @return the bodyMapper result combined with the component resource list
*/
def flatMap(bodyMapper: Html => MultipartHtml): MultipartHtml = bodyMapper(bodyMarkup) ++ resources
/**
* Add a head and/or footer content to this object
* @param resources the resources to add
* @return a new object with the resource added
*/
def ++(resources: GenTraversableOnce[Resource]): MultipartHtml = resources.foldLeft(this)(_ :+ _)
/**
* Add a head or footer content to this object
* @param resource the resource to add
* @return a new object with the resource added
*/
def :+(resource: Resource): MultipartHtml = MultipartHtml(bodyMarkup, (resources :+ resource).distinct)
/**
* Prepend a head or footer content to this object
* @param resource the resource to add
* @return a new object with the resource added
*/
def +:(resource: Resource): MultipartHtml = MultipartHtml(bodyMarkup, (resource +: resources).distinct)
/** Get tags by resource type for injection into a template */
def renderResourcesByType(resourceType: ResourceType): Html = Html(resources.filter(_.resourceType == resourceType).mkString("\n"))
}
/** Utility methods for MultipartHtml type */
object MultipartHtml {
/** Empty MultipartHtml */
def empty = MultipartHtml(Html(""))
/** A resource that can be imported in the HTML head or footer*/
trait ResourceType
trait Resource {
def resourceType: ResourceType
}
object HeadTag extends ResourceType
object FooterTag extends ResourceType
/** A style tag */
case class StyleTag(styleUrl: String) extends Resource {
val resourceType = HeadTag
override def toString = {
val assetUrl = routes.Assets.at(styleUrl).url
s"""<link rel="stylesheet" type="text/css" media="screen" href="$assetUrl">"""
}
}
/** A script tag */
case class ScriptTag(scriptUrl: String) extends Resource {
val resourceType = FooterTag
override def toString = {
val assetUrl = routes.Assets.at(s"javascript/$scriptUrl").url
s"""<script type="text/javascript" src="$assetUrl"></script>"""
}
}
}
在此基础上构建了一个完整的架构,但我可能不应该分享太多,因为它是一个未发布的产品。
创建一个将脚本存储在可变集合中的帮助程序
另一种策略可能是让您的顶级控制器创建一个对象,将对象存储在可变数据结构中。我自己没有试过这个,但你可以这样做:
import play.twirl.api.Html
case class TagStore(id: String) {
val tags = scala.collection.mutable.Set[Html]()
def addTag(tag: Html): Unit = {
store += tag
}
}
然后在你的模板中,你可以这样做:
@(addTag: Html => Unit)
@addTag {
<script src="myscript.js"></script>
}
@* Generate HTML *@
这里的缺点是你必须以某种方式转发这个对象,如果你的部分视图的层次结构可以深入,这可能会很痛苦。