Reputation: 2424
With the new Version 2.4/2.5 of the Play Framework they moved further towards injecting everything and removing the servers state. play.Play.application()
is now deprecated. However, I need the application in my template (e.g. to get all supported languages displayed on all pages with play.i18n.Lang.availables(play.Play.application())
).
I'm aware I could:
play.Application
explicitly to all of my templates.@()(implicit app: play.Application)
. However, in my Java-Project it's not really implicit, I have to pass it every time I render the template.play.api.Play.current
.How can I inject play.Application
in my templates?
---- Update: ----
What I've tried so far, I created the following setup:
index.scala.html:
@(title: String)
@template(title) { //here is the play.Application obviously missing, however I don't want to pass it in every template - even worse in every Controller <-- this is the core of my question
Welcome to my page!
}
template.scala.html:
@(title: String)(content: Html)(implicit app: play.Application)
<html>
<head>
<title>@title</title>
</head>
<body>
<p>Is live? @app.isProd</p>
@content
</body>
</html>
Controller function:
public Result index() {
return ok(views.html.index.render("home"));
}
Upvotes: 4
Views: 5058
Reputation: 4027
I just had to look into this for Play framework 2.6.x. It is possible to inject an object into a template according to the documentation: https://www.playframework.com/documentation/2.6.x/ScalaTemplatesDependencyInjection.
I implemented a simple sample (a bit contrived) and I used scala:
test.scala.html:
@this(configuration: play.api.Configuration)
@(key: String)
config = @configuration.get[Seq[String]](key).mkString(", ")
HomeController.scala
package controllers
import javax.inject._
import play.api._
import play.api.i18n._
import play.api.mvc._
/**
* This controller creates an `Action` to handle HTTP requests to the
* application's home page.
*/
@Singleton
class HomeController @Inject()(cc: ControllerComponents, testView: views.html.test) (implicit configuration: Configuration) extends AbstractController(cc) with I18nSupport{
def test() = Action { implicit request =>
Ok(testView("play.i18n.langs"))
}
}
routes:
GET /test controllers.HomeController.test()
The configuration object gets injected into views.html.test template and the view itself is injected into the controller. Note the @this(configuration: play.api.Configuration)
statement in the template. Play will generate a class behind the template with a constructor that is injected the Configuration object.
Please note that the injected configuration into the controller doesn't have any role in this particular code. I experimented with other permutations before I found this solution ... Let's say that if you have an inner template used by an outer template called from the controller, and the inner template needs the configuration object you need to feed the configuration from the controller top down, and add an implicit configuration: play.api.Configuration
parameter in all the templates in the hierarchy path right to the template that needs it, something like this: @(message: String)(implicit messagesProvider: MessagesProvider, configuration: play.api.Configuration)
. Then the controller injected configuration is fed to the top template all the way to the template that needs it.
Upvotes: 1
Reputation: 12958
First of all, though you are asking how to inject application what you really need is configuration.
You can do something like this in play 2.5. I'll show constructor injection you may use field injection as your requirement.
import com.google.inject.Inject;
import play.Configuration;
public class MyClass{
private Configuration conf;
@Inject
public MyClass(Configuration conf){
this.conf = conf;
}
}
Now you have your configuration class. Then specifically for your requirement posted in your comment, you can do this.
List<Object> langList = conf.getList("play.i18n.langs");
//conf.getList("") returns an object array.
//You can cast to Strings (assuming you are using Java8).
List<String> languages = new ArrayList<>(langList.size());
for (Object object : langList) {
languages.add(Objects.toString(object, null));
}
Now you can have your languages list in languages
.
Upvotes: 0
Reputation: 1963
You can get an application with Play.current() or inject an application in controller like this is explained in this question. The template should get the argument of type play.Application.
It should be something like this.
The template, let's say is injectappexample.scala.html:
@(app: play.Application)
.... use the app object
The controller:
public class SomeController extends Controller {
Provider<Application> applicationProvider;
@Inject
public SomeController(Provider<Application> applicationProvider) {
this.applicationProvider = applicationProvider;
}
public Result injectAppExample() {
return ok(injectappexample.render(applicationProvider.get());
}
}
It worth to reconsider sending the application object to the template. If you should send a particular configuration property value, inject Configuration in the controller, get the value from configuration object and send it to the template. In this case injecting of application is not needed at all.
The template:
@(value: String)
.... use the value
The controller:
public class SomeController extends Controller {
Configuration configuration;
@Inject
public SomeController(Configuration configuration) {
this.configuration = configuration;
}
public Result injectAppExample() {
return ok(injectappexample.render(configuration.getString("SOME_PROPERTY"));
}
}
Upvotes: 2
Reputation: 8433
It is generally discouraged to inject the Application itself, because that makes your code very cumbersome to test. Instead, think about what you actually need and inject that directly.
If you have a number of things you need in pretty much every template, my suggestion would be to create some sort of a context class that you can inject in your controller and then pass it on to the template.
Upvotes: 0