Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>In my opinion, the fact that templates are statically typed is actually a <strong>good</strong> thing: you’re guaranteed that calling your template will not fail if it compiles.</p> <p>However, it indeed adds some boilerplate on the calling sites. But <strong>you can reduce it</strong> (without losing static typing advantages).</p> <p>In Scala, I see two ways to achieve it: through actions composition or by using implicit parameters. In Java I suggest using the <code>Http.Context.args</code> map to store useful values and retrieve them from the templates without having to explicitly pass as templates parameters.</p> <h2>Using implicit parameters</h2> <p>Place the <code>menus</code> parameter at the end of your <code>main.scala.html</code> template parameters and mark it as “implicit”:</p> <pre class="lang-html prettyprint-override"><code>@(title: String)(content: Html)(implicit menus: Seq[Menu]) &lt;html&gt; &lt;head&gt;&lt;title&gt;@title&lt;/title&gt;&lt;/head&gt; &lt;body&gt; &lt;div&gt; @for(menu&lt;-menus) { &lt;a href="#"&gt;@menu.name&lt;/a&gt; } &lt;/div&gt; @content &lt;/body&gt; &lt;/html&gt; </code></pre> <p>Now if you have templates calling this main template, you can have the <code>menus</code> parameter implicitly passed for you to the <code>main</code> template by the Scala compiler if it is declared as an implicit parameter in these templates as well:</p> <pre class="lang-html prettyprint-override"><code>@()(implicit menus: Seq[Menu]) @main("SubPage") { ... } </code></pre> <p>But if you want to have it implicitly passed from your controller you need to provide it as an implicit value, available in the scope from where you call the template. For instance, you can declare the following method in your controller:</p> <pre class="lang-scala prettyprint-override"><code>implicit val menu: Seq[Menu] = Menu.findAll </code></pre> <p>Then in your actions you’ll be able to just write the following:</p> <pre class="lang-scala prettyprint-override"><code>def index = Action { Ok(views.html.index()) } def index2 = Action { Ok(views.html.index2()) } </code></pre> <p>You can find more information about this approach in <a href="http://julien.richard-foy.fr/blog/2012/02/26/composite-user-interface-without-boilerplate-using-play-2" rel="noreferrer">this blog post</a> and in <a href="https://github.com/julienrf/composite-ui-play2" rel="noreferrer">this code sample</a>.</p> <p><strong>Update</strong>: A nice blog post demonstrating this pattern has also been written <a href="http://jazzy.id.au/default/2012/10/26/passing_common_state_to_templates_in_play_framework.html" rel="noreferrer">here</a>.</p> <h2>Using actions composition</h2> <p>Actually, it’s often useful to pass the <code>RequestHeader</code> value to the templates (see e.g. <a href="https://github.com/playframework/Play20/blob/377765d2c80ff05eb4bc3ec545cfb3fd5d1e8a28/samples/scala/websocket-chat/app/views/chatRoom.scala.html" rel="noreferrer">this sample</a>). This does not add so much boilerplate to your controller code because you can easily write actions receiving an implicit request value:</p> <pre class="lang-scala prettyprint-override"><code>def index = Action { implicit request =&gt; Ok(views.html.index()) // The `request` value is implicitly passed by the compiler } </code></pre> <p>So, since templates often receive at least this implicit parameter, you could replace it with a richer value containing e.g. your menus. You can do that by using the <a href="https://github.com/playframework/Play20/wiki/ScalaActionsComposition" rel="noreferrer">actions composition</a> mechanism of Play 2.</p> <p>To do that you have to define your <code>Context</code> class, wrapping an underlying request:</p> <pre class="lang-scala prettyprint-override"><code>case class Context(menus: Seq[Menu], request: Request[AnyContent]) extends WrappedRequest(request) </code></pre> <p>Then you can define the following <code>ActionWithMenu</code> method:</p> <pre class="lang-scala prettyprint-override"><code>def ActionWithMenu(f: Context =&gt; Result) = { Action { request =&gt; f(Context(Menu.findAll, request)) } } </code></pre> <p>Which can be used like this:</p> <pre class="lang-scala prettyprint-override"><code>def index = ActionWithMenu { implicit context =&gt; Ok(views.html.index()) } </code></pre> <p>And you can take the context as an implicit parameter in your templates. E.g. for <code>main.scala.html</code>:</p> <pre class="lang-html prettyprint-override"><code>@(title: String)(content: Html)(implicit context: Context) &lt;html&gt;&lt;head&gt;&lt;title&gt;@title&lt;/title&gt;&lt;/head&gt; &lt;body&gt; &lt;div&gt; @for(menu &lt;- context.menus) { &lt;a href="#"&gt;@menu.name&lt;/a&gt; } &lt;/div&gt; @content &lt;/body&gt; &lt;/html&gt; </code></pre> <p>Using actions composition allows you to aggregate all the implicit values your templates require into a single value, but on the other hand you can lose some flexibility…</p> <h2>Using Http.Context (Java)</h2> <p>Since Java does not have Scala’s implicits mechanism or similar, if you want to avoid to explicitly pass templates parameters a possible way is to store them in the <code>Http.Context</code> object which lives only for the duration of a request. This object contains an <code>args</code> value of type <code>Map&lt;String, Object&gt;</code>.</p> <p>Thus, you can start by writing an interceptor, as explained in <a href="https://github.com/playframework/Play20/wiki/JavaActionsComposition" rel="noreferrer">the documentation</a>:</p> <pre class="lang-java prettyprint-override"><code>public class Menus extends Action.Simple { public Result call(Http.Context ctx) throws Throwable { ctx.args.put("menus", Menu.find.all()); return delegate.call(ctx); } public static List&lt;Menu&gt; current() { return (List&lt;Menu&gt;)Http.Context.current().args.get("menus"); } } </code></pre> <p>The static method is just a shorthand to retrieve the menus from the current context. Then annotate your controller to be mixed with the <code>Menus</code> action interceptor:</p> <pre class="lang-java prettyprint-override"><code>@With(Menus.class) public class Application extends Controller { // … } </code></pre> <p>Finally, retrieve the <code>menus</code> value from your templates as follows:</p> <pre class="lang-html prettyprint-override"><code>@(title: String)(content: Html) &lt;html&gt; &lt;head&gt;&lt;title&gt;@title&lt;/title&gt;&lt;/head&gt; &lt;body&gt; &lt;div&gt; @for(menu &lt;- Menus.current()) { &lt;a href="#"&gt;@menu.name&lt;/a&gt; } &lt;/div&gt; @content &lt;/body&gt; &lt;/html&gt; </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload