Add <script> to the <head> tag from the scala template in Play Framework 2

I would like to add javascript to the <head> my webpage from tags.

These are the more equivalent scripts I use on my pages:

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

 @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 the inline tag (component), which should contain the javascript code on the web page.

My question is how to pass the <script> element from the tag to main.scala.html ?

So page.scala.html will be:

page.scala.html

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

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> displayed half the HTML page, I want to pass the <script> to the @scripts variable @scripts that it appears inside the <head> .

+4
source share
4 answers

Ok, I came up with a nicer IMHO solution.

I created the following tags:

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

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

In main.scala.html you can use:

 @(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 scripts in your templates or tags using:

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

That's good, right? :)

Maybe someone can clean or improve my code with Scala magic :)

+4
source

This is doable, but I agree with ajozwik and I will say that queuing your scripts will be easier and should work fine anyway.


What you can do is add another group of parameters, basically another (content:Html) , to your main template, which will display the <script> tags generated with mytag .

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> 

In any view in which you use mytag , you need to define a value for mytagScripts . It is implicit, so you do not need to define it on templates where you do not use it, it will just use the default value.

Your tag template will not change otherwise than you need to generate a <script> and save it for later use. We can use the Context.args map for this.

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, your page will look something like this. Notice the second set of curly braces that defines mytagScripts in the main template. Closing / opening brackets between groups must be on the same line or you will get a compiler error.

page.scala.html

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

A simplified example ... if you expect to use the tag several times within the page, you will need to track the <script> tags in the List or something like that. However, since you can save any Object in Context.args .

0
source

Why don't you just go in mytag to main :

page.scala.html :

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

I did something similar in Playscala 2.4, but having the scripts included in the footer, right in front of the body closing tag. I am not familiar with the Play java api, but I believe the concept still works.

Here is the gist: https://gist.github.com/zv3/2dad7cb63813e82f8412

Controllers / 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 } } } 

opinions / support / 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

 @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 the composition of the game action) with the case class, which has TrieMap as one of its members, which will then serve as the holder of additional attributes attached to the request, and these attributes can be javascript elements and much more that you could would wish and share during the request.

0
source

Source: https://habr.com/ru/post/1501629/


All Articles