Playframework: Use Scalatags instead of Twirl

1k Views Asked by At

I'd prefer to use the former over the latter, but am not sure how to incorporate Scalatags into the playframework.

This is my simple layout:

object Test
{
  import scalatags.Text.all._
  def build =
  {

    html(
      head(
        title := "Test"
      ),
      body(

        h1("This is a Triumph"),
        div(
          "Test"
        )
      )
    )
  }
}

This is how I try to render it:

Ok(views.Test.build.render)

Problem is, that I get it as a plain String, not as HTML.

Now, of course one solution would be to simply append.

Ok(views.Test.build.render).as("text/html")

but is that really the only way? (Without creating a helper method that is)

1

There are 1 best solutions below

1
danielnixon On BEST ANSWER

I assume you want to be able to call Ok(views.Test.build). Play is ignorant of ScalaTags so we're going to have to write something ourselves here.

Play uses a bit of implicit machinery to generate HTTP responses. When you call Ok(...) you're actually calling apply on the play.api.mvc.Results trait. Let's take a look at its signature:

def apply[C](content: C)(implicit writeable: Writeable[C]): Result

So we can see that we need an implicit Writeable[scalatags.Text.all.Tag]:

implicit def writeableOfTag(implicit codec: Codec): Writeable[Tag] = {
  Writeable(tag => codec.encode("<!DOCTYPE html>\n" + tag.render))
}

Don't forget to include a doctype declaration. ScalaTags doesn't give you one.

That call to Writeable.apply itself requires another implicit to determine the content type. Here is its signature:

def apply[A](transform: A => ByteString)(implicit ct: ContentTypeOf[A]): Writeable[A]

So let's write an implicit ContentTypeOf[Tag]:

implicit def contentTypeOfTag(implicit codec: Codec): ContentTypeOf[Tag] = {
  ContentTypeOf[Tag](Some(ContentTypes.HTML))
}

This allows us to avoid having to write as("text/html") explicitly and it includes the charset (courtesy of the implicit codec), resulting in a Content-Type header of text/html; charset=utf-8.

Putting it all together:

import play.api.http.{ ContentTypeOf, ContentTypes, Writeable }
import play.api.mvc.Results.Ok
import scalatags.Text.all._

def build: Tag = {
  html(
    head(
      title := "Test"
    ),
    body(

      h1("This is a Triumph"),
      div(
        "Test"
      )
    )
  )
}

implicit def contentTypeOfTag(implicit codec: Codec): ContentTypeOf[Tag] = {
  ContentTypeOf[Tag](Some(ContentTypes.HTML))
}

implicit def writeableOfTag(implicit codec: Codec): Writeable[Tag] = {
  Writeable(tag => codec.encode("<!DOCTYPE html>\n" + tag.render))
}

def foo = Action { implicit request =>
  Ok(build)
}

You probably want to tuck those implicits somewhere convenient and then import them in your controller(s).