如何避免在play2中到处传递参数? [英] How to avoid passing parameters everywhere in play2?
问题描述
在play1中,我通常会获得动作中的所有数据,并直接在视图中使用它们.由于我们不需要在视图中显式声明参数,因此非常简单.
In play1, I usually get all data in actions, use them directly in views. Since we don't need to explicitly declare parameters in view, this is very easy.
但是在play2中,我发现我们必须在视图的头部声明所有参数(包括request
),要获取所有数据并将其传递到视图中将非常无聊.
But in play2, I found we have to declare all the parameters(including request
) in the head of views, it will be very boring to get all data in actions and pass them into views.
例如,如果需要在首页显示从数据库加载的菜单,则必须在main.scala.html
中定义它:
For example, if I need to display menus which are loaded from database in the front page, I have to define it in main.scala.html
:
@(title: String, menus: Seq[Menu])(content: Html)
<html><head><title>@title</title></head>
<body>
<div>
@for(menu<-menus) {
<a href="#">@menu.name</a>
}
</div>
@content
</body></html>
然后我必须在每个子页面中声明它:
Then I have to declare it in every sub page:
@(menus: Seq[Menu])
@main("SubPage", menus) {
...
}
然后,我必须获取菜单并将其传递给每个动作以进行查看:
Then I have to get the menus and pass it to view in every action:
def index = Action {
val menus = Menu.findAll()
Ok(views.html.index(menus))
}
def index2 = Action {
val menus = Menu.findAll()
Ok(views.html.index2(menus))
}
def index3 = Action {
val menus = Menu.findAll()
Ok(views.html.index(menus3))
}
目前,它只是main.scala.html
中的一个参数,如果有很多,该怎么办?
For now it's only one parameter in main.scala.html
, what if there are many?
所以最后,我决定直接查看所有Menu.findAll()
:
So at last, I decided to all Menu.findAll()
directly in view:
@(title: String)(content: Html)
<html><head><title>@title</title></head>
<body>
<div>
@for(menu<-Menu.findAll()) {
<a href="#">@menu.name</a>
}
</div>
@content
</body></html>
我不知道这是好还是推荐,对此是否有更好的解决方案?
I don't know if it is good or recommended, is there any better solution for this?
推荐答案
在我看来,模板是静态类型的事实实际上是好的事情:您可以保证调用模板会如果编译就不会失败.
In my opinion, the fact that templates are statically typed is actually a good thing: you’re guaranteed that calling your template will not fail if it compiles.
但是,它确实在调用站点上增加了一些样板.但是您可以减少它(而不会失去静态键入优势).
However, it indeed adds some boilerplate on the calling sites. But you can reduce it (without losing static typing advantages).
在Scala中,我看到两种方法来实现它:通过动作组合或使用隐式参数.在Java中,我建议使用Http.Context.args
映射存储有用的值,并从模板中检索有用的值,而不必显式传递为模板参数.
In Scala, I see two ways to achieve it: through actions composition or by using implicit parameters. In Java I suggest using the Http.Context.args
map to store useful values and retrieve them from the templates without having to explicitly pass as templates parameters.
将menus
参数放在main.scala.html
模板参数的末尾,并将其标记为隐式":
Place the menus
parameter at the end of your main.scala.html
template parameters and mark it as "implicit":
@(title: String)(content: Html)(implicit menus: Seq[Menu])
<html>
<head><title>@title</title></head>
<body>
<div>
@for(menu<-menus) {
<a href="#">@menu.name</a>
}
</div>
@content
</body>
</html>
现在,如果您有调用此主模板的模板,则也可以将Scala编译器将menus
参数隐式传递给main
模板,前提是这些参数也被声明为这些模板中的隐式参数:
Now if you have templates calling this main template, you can have the menus
parameter implicitly passed for you to the main
template by the Scala compiler if it is declared as an implicit parameter in these templates as well:
@()(implicit menus: Seq[Menu])
@main("SubPage") {
...
}
但是,如果要从控制器隐式传递它,则需要将其提供为隐式值,该值可在调用模板的范围内使用.例如,您可以在控制器中声明以下方法:
But if you want to have it implicitly passed from your controller you need to provide it as an implicit value, available in the scope from where you call the template. For instance, you can declare the following method in your controller:
implicit val menu: Seq[Menu] = Menu.findAll
然后,您可以在操作中编写以下内容:
Then in your actions you’ll be able to just write the following:
def index = Action {
Ok(views.html.index())
}
def index2 = Action {
Ok(views.html.index2())
}
您可以在此代码示例.
更新:此处.
实际上,将RequestHeader
值传递给模板通常很有用(请参见例如
Actually, it’s often useful to pass the RequestHeader
value to the templates (see e.g. this sample). This does not add so much boilerplate to your controller code because you can easily write actions receiving an implicit request value:
def index = Action { implicit request =>
Ok(views.html.index()) // The `request` value is implicitly passed by the compiler
}
因此,由于模板通常至少会收到此隐式参数,因此您可以将其替换为包含例如的更丰富的值.您的菜单.您可以通过使用Play 2的动作组成机制来做到这一点.
So, since templates often receive at least this implicit parameter, you could replace it with a richer value containing e.g. your menus. You can do that by using the actions composition mechanism of Play 2.
为此,您必须定义Context
类,并包装基础请求:
To do that you have to define your Context
class, wrapping an underlying request:
case class Context(menus: Seq[Menu], request: Request[AnyContent])
extends WrappedRequest(request)
然后,您可以定义以下ActionWithMenu
方法:
Then you can define the following ActionWithMenu
method:
def ActionWithMenu(f: Context => Result) = {
Action { request =>
f(Context(Menu.findAll, request))
}
}
可以这样使用:
def index = ActionWithMenu { implicit context =>
Ok(views.html.index())
}
您可以将上下文作为模板中的隐式参数.例如.对于main.scala.html
:
And you can take the context as an implicit parameter in your templates. E.g. for main.scala.html
:
@(title: String)(content: Html)(implicit context: Context)
<html><head><title>@title</title></head>
<body>
<div>
@for(menu <- context.menus) {
<a href="#">@menu.name</a>
}
</div>
@content
</body>
</html>
使用动作组合使您可以将模板所需的所有隐式值聚合为一个值,但是另一方面,您可能会失去一些灵活性...
Using actions composition allows you to aggregate all the implicit values your templates require into a single value, but on the other hand you can lose some flexibility…
由于Java没有Scala的隐式机制或类似机制,因此,如果要避免显式传递模板参数,一种可能的方法是将它们存储在Http.Context
对象中,该对象仅在请求期间有效.该对象包含类型为Map<String, Object>
的args
值.
Since Java does not have Scala’s implicits mechanism or similar, if you want to avoid to explicitly pass templates parameters a possible way is to store them in the Http.Context
object which lives only for the duration of a request. This object contains an args
value of type Map<String, Object>
.
因此,您可以先编写拦截器,如文档中所述:
Thus, you can start by writing an interceptor, as explained in the documentation:
public class Menus extends Action.Simple {
public Result call(Http.Context ctx) throws Throwable {
ctx.args.put("menus", Menu.find.all());
return delegate.call(ctx);
}
public static List<Menu> current() {
return (List<Menu>)Http.Context.current().args.get("menus");
}
}
static方法只是从当前上下文中检索菜单的简写.
然后注释您的控制器以与Menus
动作拦截器混合:
The static method is just a shorthand to retrieve the menus from the current context.
Then annotate your controller to be mixed with the Menus
action interceptor:
@With(Menus.class)
public class Application extends Controller {
// …
}
最后,如下所示从模板中检索menus
值:
Finally, retrieve the menus
value from your templates as follows:
@(title: String)(content: Html)
<html>
<head><title>@title</title></head>
<body>
<div>
@for(menu <- Menus.current()) {
<a href="#">@menu.name</a>
}
</div>
@content
</body>
</html>
这篇关于如何避免在play2中到处传递参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!