2

In Play Framework inside of a template I can define functions like that:

@title(text: String) = @{
  text.split(' ').map(_.capitalize).mkString(" ")
}

<h1>@title("hello world")</h1>

Can I define such function in separate file and make it reusable between multiple templates?

szymond
  • 1,311
  • 2
  • 19
  • 41

2 Answers2

3

There are at least several solutions:

First is using tags as Makis showed you and in most cases it's ok, even if it adds additional empty lines it doesn't matter (at least when we are talking about generating HTML snippets i.e. representing repeating news items on list) - you can i.e. optimize/compress your HTML in post-process if you care about transfer rates.

Second is creating a Scala object which contains static functions (in Java it's also possible) and returns the play.twirl.api.Html("<div>your code</div>"), this way you can programmatically fit your code exactly as you need, avoid empty lines, remove or replace spaces, etc. Just in your template you will need to use syntax like: @full.qualified.path.to.Object.method("foo", "bar") returning the Html("...") causes that you do not need to escape returned code within your view.

Third approach is using own TemplateMagic-like methods which allows you to extend objects of any type and perform some logic on it, good sample is "active".when(someCondition) syntax, you can see as it's done in the repo. Of course you need to just create custom class like this and the import it on the beginning of your view(s). Also keep in mind that it doesn't need to be only one Boolean argument, your methods can use as many parameters as you need - any type you need. It can be just parameterless.

edit - sample:

app/myapi/MyMagic.scala

package myapi

import play.twirl.api.Html

import scala.language.implicitConversions

object MyMagic {

  class RichString(string: String) {

    def upperWords() = {
      Html(string.split(' ').map(_.capitalize).mkString(" "))
    }

    def bold() = {
      Html("<b>" + string + "</b>")
    }

    def when(predicate: => Boolean) = {
      predicate match {
        case true => string
        case false => ""
      }
    }

  }

  implicit def richString(string: String): RichString = new RichString(string)
}

usage in view(s)

@(title: String)
@import myapi.MyMagic._

@main("Welcome to Play") {

    @title.upperWords() <br>
    @{"foo bar baz".upperWords()} <br>
    @{"I'm bolded LOL".bold()} <br>

    <ul>
        @for(item <- 1 to 10){
            <li >Item no.: @{item} @{"(first item)".when(item==1)}</li>
        }
    </ul>    
}
biesior
  • 55,576
  • 10
  • 125
  • 182
  • In second case I think the methods can return for example just a String. In that case I would not need to use Html() to avoid escaping. – szymond Jan 07 '16 at 11:33
  • By the way, the question was about having reusable functions in templates - it looks like it is impossible to have that. I think you should put that in the answer before mentioning how to overcome the issue. – szymond Jan 07 '16 at 11:37
1

You could create a new file named title.scala.html with these contents:

@(text: String)

@title(text: String) = @{
    text.split(' ').map(_.capitalize).mkString(" ")
}

@title(text)

Then you can use it in your templates as you would like.

Makis Arvanitis
  • 1,175
  • 1
  • 11
  • 18
  • But the result is going to contain all those new lines which appear in title.scala.html. Is there a way to avoid that? – szymond Jan 06 '16 at 12:04
  • no I don't think so, not with this approach, but the whitespace shouldn't affect the appearance of your html page though. – Makis Arvanitis Jan 06 '16 at 12:25