Chris Balogh
Chris Balogh

Reputation: 408

Why does html constructed within play 2 scala templates produce a empty Html case class

Why does this output "html is empty"?

@h = {<br />}

@h match {
  case Html("") => {html is empty}
  case _ => {html has content}
}

and this outputs "html has content"?

@i = @{Html("<br />")}

@i match {
  case Html("") => {html is empty}
  case _ => {html has content}
}

The reason this matters to me is because in the common use cases last example (moreScripts and moreStyles equivalents), the first style is used to pass in a chuck of html into another template. I then want to switch based on if that html has content or not. But it always matches to Html("").

Upvotes: 2

Views: 1126

Answers (2)

Chris Balogh
Chris Balogh

Reputation: 408

The code generated for each of the methods is slightly different.

def h:play.api.templates.Html = {
  _display_(
    Seq[Any](format.raw("""<br />"""))
  )
}
def i = {{Html("<br />")}}

The _display_ method used in creating h ends up performing a foldLeft on all the elements of the passed in Seq

This results in:

Html("") + Html("<br />")

The Html case class turns out to be backed by a mutable StringBuilder.

case class Html(text: String) extends Appendable[Html] with Content with play.mvc.Content {
  val buffer = new StringBuilder(text)

  /**
   * Appends this HTML fragment to another.
   */
  def +(other: Html): Html = {
    buffer.append(other.buffer)
    this
  }
  override def toString = buffer.toString

  /**
   * Content type of HTML (`text/html`).
   */
  def contentType: String = "text/html"

  def body: String = toString

}

This means that the value of text is only ever going to be set to the value of the first Html's text value. Whenever you create a new Html via the + method you only modify the StringBuilder.

E.g.

val html = Html("1") + Html("2")
html.text == "1"
html.toString == "12"
html.body == "12"

Since it is the text value that is used for pattern matching this effectively breaks its ability to be used in pattern matching.

E.g.

(Html("1") + Html("2")) match { case Html("1") => "broken" } // returns "broken"

Upvotes: 3

estmatic
estmatic

Reputation: 3449

Maybe try doing a string match?

@h.toString() match {
  case "" => {html is empty}
  case _ => {html has content}
}

Upvotes: 0

Related Questions