Search code examples
playframeworkscala-collectionstwirlplayframework-2.5

How do you create a Scala collection of content in a Play template?


I'm sure I'm missing the obvious but is there a way to create a collection of Html objects to pass to a Play template like @(title: String)(content: Seq(Html))? I am trying to dynamically generate a group of tab pages where each Html object is the body of the tab.

Where my confusion lies is with how to create the Seq(Html) in the template. Something like @main("Home") Seq({...},{...}) doesn't work and using @(title: String)(contentTab1: Html)(contentTab2: Html) and @main("Home") {...}, {...} defeats the purpose.

Something like:

Tab content(tab.scala.html):

@(content: Html, tab: models.Tab, isActive: String)
<section class="mdl-layout__tab-panel @isActive" id="[email protected]">
      <div class="page-content">@content</div>
</section>

Main template (main.scala.html):

@main(title: String, tabs: List[models.Tab])(contentSeq: Seq[Html])

Page template:

@(tabs: List[models.Tab])
@main("title", tabs) {
    <!-- tab1 content -->
} {
  <!-- tab2 content -->
}

*ignore bad design of matching Seq[Html] and List[models.Tab] sizes


Solution

  • Compiled Play templates are basically just functions (or rather, objects with an apply(...) method), so assuming you have a single (partial) template for the tab content you can just apply it to the tab data using map, e.g:

    Main template (main.scala.html):

    @main(title: String, tabs: Seq[Html])(content: Html)
    
    <h1>@title</h1>
    <ul class="tabs">
       @tabs.map { tab =>
         @tab
       }
    </ul>
    @content
    

    Tab content (tab.scala.html):

    @(name: String)
    
    <li>@name</li>
    

    So each page template would look something like:

    @(title: String)
    
    @main(title, Seq("tab1", "tab2", "tab3").map(name => views.html.tab(name)) {
        <p>This is some content</p>
    }
    

    ... or if you have more than one tab partial:

    @main(title, Seq(views.html.tab1("tab1"), views.html.tab2("tab2"))) {
        <p>This is some content</p>
    }
    

    Yet another way is to render each tab as a reusable block and pass them to the master template as a sequence:

    @(title: String)
    
    @tab1 = {
        <li>Content for tab 1</li>
    }
    
    @tab2 = {
        <li>Content for tab 2</li>
    }
    
    @main(title, Seq(tab1, tab2)) {
        <p>This is some content</p>
    }