在play1中,我通常在actions中获取所有数据,直接在views中使用它们。由于我们不需要在 View 中显式声明参数,因此这非常简单。
但是在play2中,我发现我们必须在 View 的头部声明所有参数(包括request
),在actions中获取所有数据并将它们传递到 View 中会非常无聊。
例如,如果我需要在首页显示从数据库加载的菜单,我必须在 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>
然后我必须在每个子页面中声明它:
@(menus: Seq[Menu])
@main("SubPage", menus) {
...
}
然后我必须获取菜单并将其传递给每个操作中的查看:
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
中只有一个参数,如果有多个参数怎么办?
所以最后,我决定将所有 Menu.findAll()
直接放在 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>
不知道好不好,是否值得推荐,有没有更好的解决方案?
最佳答案
在我看来,模板是静态类型的这一事实实际上是一件好事:保证您在编译时调用模板不会失败。
但是,它确实在调用站点上添加了一些样板。但是您可以减少它(不会失去静态类型优势)。
在 Scala 中,我看到有两种方法可以实现它:通过操作组合或使用隐式参数。在Java中我建议使用Http.Context.args
映射以存储有用的值并从模板中检索它们,而无需显式作为模板参数传递。
使用隐式参数
放置 menus
main.scala.html
末尾的参数模板参数并将其标记为“隐式”:
@(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>
现在,如果您有模板调用此主模板,则可以拥有 menus
参数隐式传递给 main
如果 Scala 编译器也将模板声明为这些模板中的隐式参数:
@()(implicit menus: Seq[Menu])
@main("SubPage") {
...
}
但是,如果您希望从 Controller 隐式传递它,则需要将其作为隐式值提供,该值在调用模板的范围内可用。例如,您可以在 Controller 中声明以下方法:
implicit val menu: Seq[Menu] = Menu.findAll
然后在您的操作中,您将能够编写以下内容:
def index = Action {
Ok(views.html.index())
}
def index2 = Action {
Ok(views.html.index2())
}
您可以在this blog post中找到有关此方法的更多信息。并在 this code sample .
更新:还写了一篇很好的博客文章来演示这种模式 here .
使用 Action 组合
实际上,传递 RequestHeader
通常很有用。模板的值(参见例如 this sample )。这不会向您的 Controller 代码添加太多样板,因为您可以轻松编写接收隐式请求值的操作:
def index = Action { implicit request =>
Ok(views.html.index()) // The `request` value is implicitly passed by the compiler
}
因此,由于模板通常至少会收到此隐式参数,因此您可以将其替换为包含例如的更丰富的值。你的菜单。您可以使用 actions composition 来做到这一点Play 2的机制。
为此,您必须定义您的 Context
类,包装底层请求:
case class Context(menus: Seq[Menu], request: Request[AnyContent])
extends WrappedRequest(request)
然后你可以定义如下ActionWithMenu
方法:
def ActionWithMenu(f: Context => Result) = {
Action { request =>
f(Context(Menu.findAll, request))
}
}
可以这样使用:
def index = ActionWithMenu { implicit context =>
Ok(views.html.index())
}
您可以将上下文作为模板中的隐式参数。例如。对于 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>
使用操作组合允许您将模板所需的所有隐式值聚合为单个值,但另一方面您可能会失去一些灵 active ......
使用 Http.Context (Java)
由于 Java 没有 Scala 的隐式机制或类似机制,如果您想避免显式传递模板参数,一种可能的方法是将它们存储在 Http.Context
中。仅在请求期间存在的对象。该对象包含 args
类型值Map<String, Object>
.
因此,您可以从编写拦截器开始,如 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");
}
}
静态方法只是从当前上下文中检索菜单的简写。
然后将您的 Controller 注释为与 Menus
混合。 Action 拦截器:
@With(Menus.class)
public class Application extends Controller {
// …
}
最后,检索menus
您的模板的值如下:
@(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>
关于playframework-2.0 - 如何避免在play2中到处传递参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9629250/