2

since PlayFramework is moving like year from global state using dependency injection and none is probably able there to rewrite guides/tutorials I can not find answer to my question anywhere.

Since Play 2.5 you have to stay away from global state even in twirl templates. Then for example: you want to use WebJarAssets in your template then you have to use according this guide (http://www.webjars.org/documentation):

controller in Java:

public class Application extends Controller {

    @Inject WebJarAssets webJarAssets;

    public Result index() {
        return ok(index.render(webJarAssets));
    }

}

and template:

@(webJarAssets: WebJarAssets)
<!DOCTYPE html>
<html>
    <head>
        <link rel='stylesheet' href='@routes.WebJarAssets.at(webJarAssets.locate("css/bootstrap.min.css"))'>
    </head>
    <body>
    </body>
</html>

Ok that make sense and can be easy done. But your template have usually more then one parameter and now there is MeesageApi which you might want to use also in template and any other.. then in Java you will have to pass everything as parameter and your template will have thousand of parameters and will be hard to read and manage. Now if you move from older version like Play2.4 and less you need to edit parameters for every template you made probably and then also every controller and if your project is really big this is so much unnecessary work..

So there should be DI available also in templates. This issue should be already fixed here: Injectable templates

So I followed what was written there and added to plugins.sbt:

addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.2.0")

added to build.sbt:

TwirlKeys.constructorAnnotations += "@javax.inject.Inject()"

also I have resolver as following:

resolvers += Resolver.sonatypeRepo("snapshots")

Then I added DI to twirl template:

@import play.twirl.api.HtmlFormat
@import b3.vertical.fieldConstructor
@import views.html.menu.menu
@import controllers.WebJarAssets
@(title: String)(implicit headInsert: Html = HtmlFormat.empty, content: Html = HtmlFormat.empty)

@this(webJarAssets: WebJarAssets) 

Restarted play and I am still getting error:

play.sbt.PlayExceptions$CompilationException: Compilation error[not found: value webJarAssets]

What did I miss? Should I also inject something inside controller? Is there anywhere example of play app, which use DI everywhere?

Martin Dluhy
  • 143
  • 1
  • 10
  • I think you have to inject the view in the controller as well. Otherwise the template won't be handled by the DI framework. See the comment at the end of the issue how to do this in java: https://github.com/playframework/twirl/pull/100#issuecomment-265736004 Additionally you might want to check if `@this()` has to be placed before `@(params)`. – Alexander B Jan 16 '17 at 16:15
  • 1
    Thanks, actually @this() had to be placed before @params – Martin Dluhy Jan 19 '17 at 01:24
  • Great that you solved it. I'll add it as an answer so that you can accept it to help others who have the same issue. – Alexander B Jan 24 '17 at 07:38

1 Answers1

1

The ordering of the constructor call @this() with the dependency injected values and the parameters for rendering the template is important. You should place @this() before the regular template parameters.

@import play.twirl.api.HtmlFormat
@import b3.vertical.fieldConstructor
@import views.html.menu.menu
@import controllers.WebJarAssets

@this(webJarAssets: WebJarAssets)

@(title: String)(implicit headInsert: Html = HtmlFormat.empty, content: Html = HtmlFormat.empty)
Alexander B
  • 1,023
  • 7
  • 20