如何使用play framework 2中的脚本处理多层模板?

时间:2014-08-28 20:01:41

标签: scala playframework-2.0 template-engine

我有这样的模板结构:

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的脚本,但是我找不到将脚本模板中的脚本传递给父模板的方法,以便将它们呈现在主模板的正确位置。有任何想法吗?提前谢谢!

1 个答案:

答案 0 :(得分:1)

我不认为Play团队已经祝福你的问题有一个规范的答案,但我可以想到几种方法:monadic方法和命令式方法。

在子控制器中包裹视图;在输出中封装脚本

我正在开展的一个大项目使用这种策略。我们创建了一个MultipartHtml类型,其中包含应包含在文档正文中的Html输出以及我们创建的名为Resources的类型,其中包含应该转到别处的内容。我们将此类型视为monad,因此我们可以mapflatMap操纵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 *@

这里的缺点是你必须以某种方式转发这个对象,如果你的部分视图的层次结构可以深入,这可能会很痛苦。