tl;dr
The answers given so far (by @wbarksdale, @PlexQ and @Daniel C. Sobral in a comment) are good enough to target the problem described here.
But they're missing a real explanation about why the initial code, using foreach
, doesn't work.
It cannot work because foreach
returns Unit
.
Play concepts
Let me give a quick note/recall about how templates work.
The Scala templating system provided by default in Play Framework 2 is indeed built upon FP concepts and thus it uses a lot of immutable structures and so forth.
Moreover, such Scala template (let's say myTemplate.scala.html
) will be compiled into a regular Scala object
which has an apply
method called. This latter function enables us to call the object as a function with some parameters (those declared in the first line of the template).
This object
is also relying on a construction like BaseScalaTemplate
which is built with an output formatter (Html). This formatter will be able to take stuff (like a String
, Unit
, Seq[Int]
, Map[A,B]
, ...) and render it into HTML code.
Formatting will take place while using the _display_
method of BaseScalaTemplate
, which returns an instance of the formatted output. This display method will be called in the compiled code of the .scala.html
file in the object's apply
method's body.
So the body could end like that:
def apply/*1.2*/(testMap:scala.collection.immutable.Map[String, Int]):play.api.templates.Html =
_display_ {
Seq[Any](
_display_(
Seq[Any](
/*3.2*/testMap/*3.9*/.map/*3.13*/ { e =>
_display_(Seq[Any](_display_(Seq[Any](/*5.3*/e))))
}
)
)
)
}
See? The _display_
calls aren't mutating anything but they are composed in such a way that apply itself will return an instance of the formatted code (Html
)!
That gives us the clue...
yeah blah blah... now why?
After those lightnings given about the Play internals, we can now tackle the real question: why the hell is the ideomatic Scala code provided in the question post isn't working... read, doesn't output anything at all.
It's pretty simple, when using foreach
on a Map
, you're indeed looping over the items and adapting them to Html. But these computation won't be usable by the templating system because they are enclosed in the foreach
's loop. That is foreach
has to be used when side-effects are required for each item in the sequence... And return Unit
when it's done.
Since, the templating system will try to _display_
the result of foreach
on the given Map
it will simply render/format Unit
and thus an empty String
!
To conclude, just use map
which will return a new sequence holding the adapted items, the Html
instance.
Hmmm and what about the for
?
Yeah, you're right... Based on what has been said, why are the answers that proposed to used a for
loop working, since without yielding a value, a for
is equivalent to foreach
!? (Introducing yield
will end in a map
-like behavior)
The answer is in the code... The Template compiler will prepend the yield
keyword to the for
's body -- check this out here. :-D
Et voilà, it works too, since the generated stuffs in the for
's body will be attached to a returned sequence after it has finished.