kes
kes

Reputation: 6177

Mapping a list to HTML and intercalating the result in Play 2.0 template

Given a list of foobars (each of which contain a name, and a slug used to create a URL hyperlink), what would be the idiomatic way in a Play 2.0 template to render each item in this list to a link (or some other HTML), and then intercalate the list with some character, such a comma?

For example, if there were three foobars, the final output might look like this:

<a href="/quux/foobar1">Foobar1</a>, <a href="/quux/foobar2">Foobar2</a>, <a href="/quux/foobar3">Foobar3</a>

My first impulse was to try:

@foobars.map{foobar => 
<a href="@routes.Foobars.quux(foobar.slug)">@foobar.name</a>
}.mkString(", ")

but this outputs escaped HTML, which is not what I want.

This seems like it would be a common use case; is there an idiomatic way it can be accomplished?

Upvotes: 4

Views: 773

Answers (4)

Patrik Beck
Patrik Beck

Reputation: 2505

I was able to solve this for Play 2.3 as well, using combination of other answers:

@foobars.map{foobar =>
    <a href="@routes.Foobar.quux(foobar.slug)">@foobar.name</a>
}.reduceLeftOption((e1: Html, e2: Html) => Html(e1 + "," + e2))

Upvotes: 0

ludwigm
ludwigm

Reputation: 3383

Based on Debilski's idea I built a working version. But sadly I get an extra space when appending. Not a big deal, but it is there:

<td>
    @r.countries.map{ country =>
      <a href="@routes.Items.editCountry(country.toString, "main")">
         @Country.findOneByID(country).map(_.name)
      </a>
    }.reduceLeftOption((e1: Html, e2: Html) => e1 += Html(",") += e2)
</td>

Upvotes: 1

Grant Klopper
Grant Klopper

Reputation: 81

You should be able to use the Html helper to stop escaping...

@{Html(foobars.map{foobar => 
    <a href="@routes.Foobars.quux(foobar.slug)">@foobar.name</a>
}.mkString(", "))}

Upvotes: 2

Debilski
Debilski

Reputation: 67888

I haven’t used Play but I think that mkString seems to be the problem. It transforms your painstakingly built NodeSeq into a String which gets then passed to Play’s HTML output routines where it gets transformed to a Text (escaping included).

To avoid this:

@foobars.map{foobar => 
  <a href="@routes.Foobars.quux(foobar.slug)">@foobar.name</a>
}.reduceLeft((e1: xml.NodeSeq, e2: xml.NodeSeq) => e1 ++ xml.Text(", ") ++ e2)

(This will fail for an empty sequence of foobar. You’d have to look into reduceLeftOption then.)

There might be a dedicated function for this in the Play API, though.

Upvotes: 1

Related Questions