Is it possible to prettify scala templates using play framework 2?
Asked Answered
S

5

19

Using Play Framework 2 I've noticed the rendered Scala HTML templates don't like indented @if or @for.

So, for example, something like that:

<ul>
   @for(test <- tests) {
      <li>@test.name</li>
   }
</ul>

Will have extra unneeded spaces. To fix it, I need to do something like that:

<ul>
@for(test <- tests) {
   <li>@test.name</li>
}
</ul>

Which will get messy with additional @defining or other statements.

So, is there a way to prettify/beautify Scala templates rendering in order to get rid of extra white spaces?

UPDATE:

Reading this thread I've noticed extra spaces and line breaks are added as well because of the parameters on top of the templates. So this:

@(myParam: String)


<!DOCTYPE html>
<html>
   <head></head>
   <body></body>
</html>

will add 3 extra line breaks on top of the resulting HTML. Which is definitely annoying.

The thread seems to say there are no option at the moment to correct that.

Squawk answered 4/1, 2013 at 9:46 Comment(0)
S
17

So for more details I've used @biesor answer and went through these steps:

Add HtmlCompressor as a plugin

In Build.scala:

val appDependencies = Seq(
    "com.googlecode.htmlcompressor" % "htmlcompressor" % "1.5.2"
)

PrettyController

public class PrettyController extends Controller {

    public static Results.Status ok(Content content) {
        return Results.ok(prettify(content)).as("text/html; charset=utf-8");        
    }

    public static Results.Status badRequest(Content content) {
        return Results.badRequest(prettify(content)).as("text/html; charset=utf-8");        
    }

    public static Results.Status notFound(Content content) {
        return Results.notFound(prettify(content)).as("text/html; charset=utf-8");      
    }

    public static Results.Status forbidden(Content content) {
        return Results.forbidden(prettify(content)).as("text/html; charset=utf-8");     
    }

    public static Results.Status internalServerError(Content content) {
        return Results.internalServerError(prettify(content)).as("text/html; charset=utf-8");       
    }

    public static Results.Status unauthorized(Content content) {
        return Results.unauthorized(prettify(content)).as("text/html; charset=utf-8");      
    }

    private static String prettify(Content content) {
        HtmlCompressor compressor = new HtmlCompressor();
        String output = content.body().trim();

        if (Play.isDev()) {
            compressor.setPreserveLineBreaks(true);
        }

        output = compressor.compress(output);

        return output;
    }
}

Then every controllers should extend PrettyController.

Squawk answered 8/1, 2013 at 9:54 Comment(2)
I like this, avoids DRY violation, maybe you could create sample app for others on the Github ?Paramecium
I wonder if this could be done at compile time, so the pretty version is the one embed in the resulting class, instead of having to prettify it on each request.Indistinct
N
8

I've released a Google HTML Compressor plugin for Play 2.1. You can find it on GitHub.

Nalley answered 29/6, 2013 at 17:13 Comment(2)
This worked well for me, thanks. I contributed an upgrade to Play 2.2.1.Frolic
Very great plugin. Easy configuration and works well with GZIP filter.Nad
P
4

Of course there is always some option :), trim the body and set header again so (cause after operations on the String it will be returned as text/plain):

// instead of
return ok(index.render("some"));

// use
return ok(index.render("some").body().trim()).as("text/html; charset=utf-8");

for 'beauty' loops or if's you need to write more compact code

// instead of
@for(test <- tests) {
  <li>@test.name</li>
}

// use
@for(test <- tests) {<li>@test.name</li>}

And finally you can use some compressor (ie. com.googlecode.htmlcompressor) to... minify whole page (in this sample for production mode only)

String output = index.render("some").body().trim();
if (Play.isProd()) output = compressor.compress(output);
return ok(output).as("text/html; charset=utf-8");
Paramecium answered 4/1, 2013 at 11:24 Comment(4)
interesting, how would you do that for every call? changing every requests for every controllers?Squawk
In play 2.1, there is a Filter (github.com/playframework/Play20/blob/master/framework/src/play/…) mechanism which will be the perfect solution to do this. But it's very complicated, you must work with the Iteratee API instead of String... I've tried few days ago, but too complicated for me !Nope
@RomainPiel Yes, but is that problem ? in existing project it's pasting from the clipboard. How many actions returning HTML have you got ? 20? 30, 5 minutes of work.Paramecium
@RomainPiel You could use helper method, or override Ok.Shekinah
M
1

I was expecting answers which truly "prettify" the HTML output, in the sense of properly indenting the output in addition to removing blank lines. However, HtmlCompressor only compresses the output, and has no pretty printing logic.

I came up with a solution that uses both HtmlCompressor for compression in production, and Jsoup for pretty-printing during development. I don't care about calling the prettify conversion explicitly, so my solution looks like this:

// required extra imports
import play.twirl.api.Html
import com.googlecode.htmlcompressor.compressor.HtmlCompressor
import org.jsoup.Jsoup
import org.jsoup.parser.Parser

@Singleton
class MyController @Inject() (environment: Environment) extends Controller {

  /** Helper to format Html */
  def prettify(content: Html): Html = {
    val rawString = content.body.trim()
    val html = environment.mode match {
      case Mode.Dev =>
        val doc = Jsoup.parse(rawString, "", Parser.xmlParser())
        doc.outputSettings().indentAmount(2)
        Html(doc.toString())
      case _ =>
        val compressor = new HtmlCompressor()
        compressor.setPreserveLineBreaks(true)
        Html(compressor.compress(rawString))
    }
    html
  }

  /** example usage */
  def index = Action {
    Ok(prettify(views.html.index))
  }

}  

In dev mode this produces some nicely formatted HTML.

The required changes to build.sbt are:

libraryDependencies += "org.jsoup" % "jsoup" % "1.10.2"
libraryDependencies += "com.googlecode.htmlcompressor" % "htmlcompressor" % "1.5.2"
Mostly answered 14/4, 2017 at 17:2 Comment(0)
A
0

Echoing on bluenote10's answer I created the following, it does not require third party libraryDependecies. It would be nice to integrate it into a filter, unfortunately I am not sure how to do that correctly today.

import play.twirl.api.Html

/** Helper to format Html */
def prettify(content: Html): Html = {
  Html(content.body.trim().replaceAll("\\n\\s*\\n", "\n"))
}

def index = Action { implicit request =>
  Ok(prettify(views.html.index()))
}
Arminius answered 7/9, 2017 at 21:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.