简体   繁体   English

加<script> to the <head> from scala template tags in Play Framework 2

[英]Add <script> to the <head> from scala template tags in Play Framework 2

I would like to add javascript to the <head> of my webpage from within tags. 我想从标签内添加javascript到我的网页的<head>

This is the moreScripts equivalent I'm using on my pages: 这是我在我的网页上使用的更多脚本:

main.scala.html main.scala.html

@(title: String, scripts: Html = Html(""))(content: Html)
<!DOCTYPE html>
<html lang="nl">
    <head>
        <title>@title</title>
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
        <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
        @scripts
    </head>
    <body>
        @content
    </body>
</html>

page.scala.html page.scala.html

@scripts = {
    <script type="text/javascript">
        $(document).ready(function() {
            alert(1);
        });
    </script>
}
@main("Title", scripts) {
    <p>page content</p>
}

So far so good! 到现在为止还挺好! However I want to do the same from within a tag (component) I've written that needs to include some javascript code into the webpage. 但是我想在我编写的标签(组件)中做同样的事情,需要在网页中包含一些javascript代码。

My question is how can I pass a <script> element from the tag to the main.scala.html ? 我的问题是如何将<script>元素从标记传递到main.scala.html

So the page.scala.html would be: 所以page.scala.html将是:

page.scala.html page.scala.html

@import tags._
@main("Title") {
    @mytag("green")
}

mytag.scala.html mytag.scala.html

@(color: String)
<script type="text/javascript">
    $(document).ready(function() {
        alert('@color');
    });
</script>

<p>Some more content</p>

In this case the <script> tag is rendered halfway the HTML page, I want to pass the <script> tag into the @scripts variable so it can be rendered inside the <head> tag. 在这种情况下, <script>标记在HTML页面的中间呈现,我想将<script>标记传递给@scripts变量,以便它可以在<head>标记内呈现。

Ok, I've come up with a nicer solution IMHO. 好的,我想出了一个更好的解决方案恕我直言。

I have created the following tags: 我创建了以下标记:

script.scala.html script.scala.html

@(content: Html)
@{
    var additionalScripts = ctx().args.get("additionalScripts").asInstanceOf[List[Html]];
    if(additionalScripts == null) {
        additionalScripts = new ArrayList[Html]();
        ctx().args.put("additionalScripts", additionalScripts)
    }

    val added = additionalScripts.add(content);
}

renderscripts.scala.html renderscripts.scala.html

@additionalScripts = @{ctx().args.get("additionalScripts").asInstanceOf[List[Html]]}
@if(additionalScripts != null) {
    @for(additionalScript <- additionalScripts) {
        @additionalScript
    }
}

In your main.scala.html you can use: main.scala.html您可以使用:

@(title: String)(content: Html)
@import tags._
<!DOCTYPE html>
<html lang="nl">
    <head>
        <title>@title</title>
        <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
        @renderscripts()
    </head>
    <body>
        @content
    </body>
</html>

You can specify additional script(s) in your templates or tags using: 您可以使用以下命令在模板或标签中指定其他脚本:

@import tags._
@script {
    <script type="text/javascript">
        $(document).ready(function() {
            alert('This will be in the head!');
        });
    </script>
}

This is nice, right? 这很好,对吧? :) :)

Maybe someone can clean up or enhance my code using their Scala magic :) 也许有人可以使用他们的Scala魔法来清理或增强我的代码:)

It's doable but I would agree with ajozwik and say that putting your scripts inline is going to be easier and should still work fine. 这是可行的,但我同意ajozwik,并说你的脚本内联将更容易,应该仍然可以正常工作。


What you can do is add another parameter group, basically another (content:Html) , to your main template which is what will render the <script> tags generated by using mytag 's. 你可以做的是添加另一个参数组,基本上是另一个(content:Html)到你的主模板,这将呈现使用mytag生成的<script>标签。

main.scala.html main.scala.html

@(title: String, scripts: Html = Html(""))(content: Html)(implicit mytagScripts: Html = null)
<!DOCTYPE html>
<html lang="nl">
  <head>
    <title>@title</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    @scripts
    @mytagScripts
  </head>
  <body>
    @content
  </body>
</html>

On any view where you're using mytag you'll need to define the value for mytagScripts . 在您使用mytag任何视图上,您​​需要定义mytagScripts的值。 It's implicit so that you don't have to define it on templates where you're not using it, it'll just use the default. 它是隐含的,因此您不必在不使用它的模板上定义它,它只使用默认值。

Your tag template isn't going to change much other than you need to generate the <script> and store it for later use. 除了需要生成<script>并将其存储以供以后使用之外,您的标记模板不会发生太大变化。 We can use the Context.args map for this. 我们可以使用Context.args映射。

mytag.scala.html mytag.scala.html

@(color:String)
@{ctx().args.put("mytagScriptHtml", new Html("<script>alert('" + color + "');</script>"))}
<div>
  <!-- whatever else you're tag is generating -->
</div>

Finally, you're page would look something like this. 最后,你的页面看起来像这样。 Note the second set of curly braces which is what is defining mytagScripts on the main template. 注意第二组花括号,它是在主模板上定义mytagScripts的。 The closing/opening braces between the groups have to be on the same line or you'll get a compiler error. 组之间的关闭/打开括号必须在同一行上,否则您将收到编译器错误。

page.scala.html page.scala.html

@import tags._
@main("Title") {
  @mytag("green")
} {
  @ctx().args.get("mytagScriptHtml")
}

Simplified example... if you're expecting to use the tag multiple times within a page then you'll need to keep track of the <script> tags in a List or something. 简化示例...如果您希望在页面中多次使用该标记,则需要跟踪List中的<script>标记或其他内容。 Same concept though since you can store any Object in Context.args . 同样的概念,因为您可以在Context.args存储任何Object

Why don't you just pass in the mytag to the main : 为什么不直接将mytag传递给main

page.scala.html : page.scala.html

@import tags.html.mytag
@main("Title", mytag("green")) {
    <p>page content</p>
}

I did something similar in Play-scala 2.4 but with having scripts going into the footer, right before the body closing tag. 我在Play-scala 2.4中做了类似的事情,但是在主体关闭标签之前有脚本进入页脚。 I'm not familiar with Play's java api but i believe the concept still works. 我不熟悉Play的java api,但我相信这个概念仍然有用。

Here is the gist: https://gist.github.com/zv3/2dad7cb63813e82f8412 这是要点: https//gist.github.com/zv3/2dad7cb63813e82f8412

controllers/StackableAction.scala 控制器/ StackableAction.scala

// Borrowed from: https://github.com/rabitarochan/Play2-ChainAction/blob/master/core/src/main/scala/com/github/rabitarochan/play2/stackableaction/StackableAction.scala
abstract class StackableAction extends ActionBuilder[RequestWithAttributes] with StackableFilter {

  override def filter[A](request: RequestWithAttributes[A])(f: RequestWithAttributes[A] => Future[Result]): Future[Result] = {
    f(request)
  }

  def invokeBlock[A](req: Request[A], block: RequestWithAttributes[A] => Future[Result]): Future[Result] = {
    val reqWA = new RequestWithAttributes(req, new TrieMap[AttributeKey[_], Any]())
    filter(reqWA)(block)
  }

}

trait StackableFilter {
  def filter[A](request: RequestWithAttributes[A])(f: RequestWithAttributes[A] => Future[Result]): Future[Result]
}

trait AttributeKey[A] {
  def ->(value: A): Attribute[A] = Attribute(this, value)
}

case class Attribute[A](key: AttributeKey[A], value: A) {
  def toTuple: (AttributeKey[A], A) = (key, value)
}

class RequestWithAttributes[A](request: Request[A], attributes: TrieMap[AttributeKey[_], Any]) extends WrappedRequest[A](request) {
  def get[B](key: AttributeKey[B]): Option[B] = attributes.get(key).asInstanceOf[Option[B]]
  def set[B](key: AttributeKey[B], value: B): RequestWithAttributes[A] = {
    attributes.put(key, value)
    this
  }
  def getAll[T](implicit classTag: ClassTag[T]) = {
    attributes.filterKeys {
      case p: T => true
      case _    => false
    }
  }
}

views/support/JavascriptPage.scala 意见/支持/ JavascriptPage.scala

object JavascriptPage {
  case class NonBlockingJS(key: String) extends AttributeKey[Html]
  case class BlockingJS(key: String) extends AttributeKey[Html]

  def addNonBlockingJS(div: String)(jscript: Html)(implicit request: Request[_]): Unit = {
    request match {
      case i: RequestWithAttributes[_] =>
        i.set(NonBlockingJS(div), jscript)
      case _ =>
    }
  }

  // scripts that are supposed to go into the <head> tag, thus blocking scripts
  def addBlockingJS(div: String)(jscript: Html)(implicit request: Request[_]): Unit = {
    request match {
      case i: RequestWithAttributes[_] =>
        i.set(BlockingJS(div), jscript)
      case _ =>
    }
  }

  // scripts that are supposed to go before the </body> tag, non blocking scripts that is
  def getNonBlockingJS()(implicit request: Request[_]): Seq[(String, Html)] = {
    request match {
      case i: RequestWithAttributes[_] =>
        i.getAll[NonBlockingJS].toSeq.map {
          case (NonBlockingJS(div), jscript: Html) => (div, jscript)
        }
      case _ => Seq.empty
    }
  }
}

inlineNonBlockingJS.scala.html inlineNonBlockingJS.scala.html

@import views.support.JavascriptPage
@(implicit request: Request[_])
<script src="/javascripts/your_javascript_app.js"></script>
<script id="non-blocking" type="text/javascript">
  @defining(JavascriptPage.getNonBlockingJS()) { scripts =>
    @scripts.map { case (_, item) => @item }
  }
</script>

It basically wraps the request (using play's action composition) with a case class that has a TrieMap as one of it's members, which would then serve as a holder of extra attributes tied to the request, and those attributes could be javascript entries and pretty much anything else that you could like to have and share during the life of a request. 它基本上将请求(使用play的动作组合)包含在一个案例类中,该案例类具有TrieMap作为其成员之一,然后它将作为与请求关联的额外属性的持有者,并且这些属性可以是javascript条目和几乎您希望在请求期间拥有和分享的任何其他内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM