Search code examples
htmlscalaplayframeworkvariadic-functionstwirl

Scala Play template vararg HtmlContent


I have a generic template in play 2.6, that I want to pass in a variable amount of HtmlContents. I've defined the template like this (including the implicit parameter I have in case that changes anything):

@(foo: String)(content: Html*)(implicit bar: Bar)

On the template side, this works fine-- I can dissect content with for and render it as I want. However, I haven't been able to figure out a clean way to invoke the variable arguments from the underlying template.

e.g, I have a view named "Baz":

@(something: String)(implicit bar: Bar)

In it, I try to invoke the template with multiple Html arguments. I've tried the following:

@template("fooString"){{123},{abc}}
and
@template("fooString")({123}, {abc})
and
@template("fooString"){{123}, {abc}})

And various other permutations, but inside of an enclosing bracket it seems to interpret everything literally as a single parameter in the HtmlContent vararg.

However, this ended up working as I intended, passing in multiple HtmlContents:

@template("fooString")(Html("123"), Html("abc"))

So that works, and I can use a triple-quoted interpolated string for a large Html block-- but it seems like there should be a cleaner way to do this, and the string interpolation is dangerous as it doesn't do html escaping.

Is there a way to do this using the { enclosed syntax? I'd like to understand more what is actually happening on an underlying level, and how play parses and generates HtmlContent in brackets.


Solution

  • So consider you have below template

    // main.scala.html
    @(title: String)(contents: Html*)
    

    There are different ways you can call this template

    Option #1

    This is what you already posted in the question

    @main("This is a title")(Html("abc"), Html("123"))
    

    Options #2

    @html1 = {
        Hello
    }
    @html2 = {
        <div>Tarun</div>
    }
    
    @main("This is a title")(html1, html2)
    

    Option #3

    @main("This is a title")(Html(<div>Tarun
    </div>.toString), Html(<div>
    Lalwani
    </div>.toString))
    

    Options #4

    This is not exactly same option, but needs change in Template signature itself

    @(title: String)(contents: List[String])  
    

    And then calling it like below

    @main("This is a title")(List(
      """<div>
         Tarun
        </div>
      """, """Hello"""))
    

    Option #5

    This requires code files and was already answered on another SO thread

    Paul Draper's answer on Why doesn't this pass multiple HTML parameters to template