7

The Play Framework 2 template language is pretty nice. However, though it’s ‘inspired by’ Microsoft’s Razor language, one important design decision is different: how you ‘escape back’ into HTML. Razor looks for HTML-style tags and Play 2 uses some kind of heuristic.

I’m trying to write a template which takes multiple ‘sections’ of HTML, and generates a page with headers and a table of contents. My ‘structuredpage.scala.html’ looks like this:

@(title: String)(sections: Pair[String,Html]*)

@main(title){
    <nav class="page-links">
        @makeTableOfContents(sections)
    </nav>
    @for(section <- sections){
        <section id="@section._1">
            <h2>@section._1</h2>
            @section._2
        </section>
    }
}

Note that its second parameter is a variable number of sections. There does not seem to be a way of calling this in the Play templating language.

I’ve created a helper function called Common.section which looks like this:

    def section(title: String)(content: Html) = title -> content;

I’ve tried this:

@()
@import views.Common.section

@structuredpage("Dashboard")(
    section("Latest Requests") {
        <p>Blah</p>
    },
    section("Your Details") {
        <p>Blah blah</p>
    }
)

…which gives type mismatch; found : scala.xml.Elem required: play.api.templates.Html on line 5, i.e., <p>Blah</p> is being interpreted as Scala, not as template document HTML.

And this:

@()
@import views.Common.section

@structuredpage("Dashboard"){
    @section("Latest Requests") {
        <p>Blah</p>
    },
    @section("Your Details") {
        <p>Blah blah</p>
    }
}

…which gives type mismatch; found : play.api.templates.Html required: (String, play.api.templates.Html) on line 3, i.e., the entire outer curley-brace block is being interpreted as template document HTML, not as Scala code!

Frustratingly they don’t seem to be hugely different than some code samples in the official Play 2 documentation, for example: http://www.playframework.org/documentation/2.0/ScalaTemplateUseCases

Any ideas? I’m using Play Framework 2.0.4

andrewf
  • 1,350
  • 1
  • 13
  • 19
  • what's the compilation error you get? – Pere Villega Dec 13 '12 at 15:03
  • Have added the compiler errors, and my interpretation of them to the question. The gist is that it’s either interpreting too much or too little of the template as Scala code. – andrewf Dec 13 '12 at 15:48

3 Answers3

1

Here's what you might be looking for. It's not exactly FP though

structuredpage.scala.html

@(title: String)(content: scala.collection.mutable.MutableList[Pair[String, Html]] => Unit)

@main(title){
    @defining(new scala.collection.mutable.MutableList[Pair[String,Html]]()) { sections =>
        @content(sections)
        @for(section <- sections){
            <section id="@section._1">
                <h2>@section._1</h2>
                @section._2
            </section>
        }
    }
}

frontpage.scala.html

@()

@import views.Common.section

@structuredpage("Front Page") { implicit sections =>
    @section("Section 1") {
        <h1>stuff</h1>
    }

    @section("Section 2") {
        <h1>more stuff</h1>
    }
}

section method:

def section(title: String)(content: Html)(implicit sections: scala.collection.mutable.MutableList[Pair[String, Html]]) {
    sections += title -> content
}
thatsmydoing
  • 863
  • 7
  • 10
  • Haha! That’s nasty and devious! True it’s not very functional, but at least most of the unpleasantness is hidden away. It works, and the section headers are in proximity to their contents. I hope the Play Framework people take another look at escaping rules in the template language, but this hack could be useful in the mean time. – andrewf Dec 16 '12 at 18:07
0

I can't test this right now in this machine, but the following should work (no need of auxiliar method):

@()

@structuredpage("Dashboard"){
   ("Latest Requests", {
        <p>Blah</p>
    }),
   ("Your Details", {
        <p>Blah blah</p>
    })
}
Pere Villega
  • 16,429
  • 5
  • 63
  • 100
  • Unfortunately not! Just tried it, and it fails in the same way as my second code example. `type mismatch; found : play.api.templates.Html required: (String, play.api.templates.Html)` on line 3. The template compiler interprets the entire contents of the outer curley-braces as HTML template material. – andrewf Dec 13 '12 at 16:02
  • wlil try to doublecheck this evening why – Pere Villega Dec 13 '12 at 17:22
  • (but of course, point taken about avoiding the additional function/template) – andrewf Dec 13 '12 at 22:58
0

Here is a workaround:

@import views.Common.section

@sec1 = { <p>Blah</p> }

@sec2 = { <p>Blah blah</p> }

@structuredpage("Dashboard")(
    section("Latest Requests")(sec1),
    section("Your Details")(sec2)
)

previous attempt:

I think your wish makes it to complicated. Templates should be simple. Here is a simple alternative:

index.scala.html

@structuredpage("Dashboard"){
    @section("Latest Requests") {
        <p>Blah</p>
    }

    @section("Your Details") {
        <p>Blah blah</p>
    }
}

section.scala.html

@(title: String)(content: Html)

<section id="@title">
    <h2>@title</h2>
    @content
</section>

structuredpage.scala.html

@(title: String)(sections: Html)

@main(title){
    <nav class="page-links">
        table-of-contents goes here
    </nav>
    @sections
}

I made a gist: https://gist.github.com/4280577 . So you can check it out and play with it.

Schleichardt
  • 7,502
  • 1
  • 27
  • 37
  • Thanks, but unfortunately that's no good! My fault for oversimplifying the original question: I'd like to build 'table of contents' dynamically from the list of 'sections', so I can't just take 'sections' as a lump of Html. I agree that templates should be as simple as possible. But in this case it's difficult to see how I can make it simpler and still do what I want. – andrewf Dec 13 '12 at 22:54
  • That could work, thanks. The title is not next to the content, but apart from that it seems to do the job. Will let you know when I’ve had a chance to try it out. – andrewf Dec 14 '12 at 11:40