在我看来,模板是静态类型的事实实际上是一件好事:如果编译成功,你可以保证调用你的模板不会失败。
然而,在调用网站时确实会增加一些样板。但是您可以减少它(而不失去静态类型的优势)。
在Scala中,我看到有两种方法可以实现:通过操作组合或使用隐式参数。 在Java中,我建议使用Http.Context.args
映射存储有用的值,并从模板中检索它们,而无需明确传递为模板参数。
使用隐式参数
将menus
参数放置在您的main.scala.html
模板参数末尾,并将其标记为“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>
如果您有调用此主模板的模板,则在这些模板中声明 menus
参数为隐式参数,Scala编译器可以自动为您将其传递到 main
模板中:
@()(implicit menus: Seq[Menu])
@main("SubPage") {
...
}
但是,如果你想要从你的控制器中隐式地传递它,你需要将它作为一个隐式值提供,在调用模板的范围内可用。例如,你可以在你的控制器中声明以下方法:
implicit val menu: Seq[Menu] = Menu.findAll
然后在你的操作中,你只需要写下以下内容:
def index = Action {
Ok(views.html.index())
}
def index2 = Action {
Ok(views.html.index2())
}
您可以在这篇博客文章和这个代码示例中找到更多关于这种方法的信息。
更新:还有一篇很好的博客文章展示了这种模式,可以在这里找到。
使用操作组合
实际上,将RequestHeader
值传递给模板通常很有用(例如,请参见这个示例)。这不会给您的控制器代码添加太多样板文件,因为您可以轻松编写接收隐式请求值的操作:
def index = Action { implicit request =>
Ok(views.html.index())
}
因此,由于模板通常至少接收此隐式参数,您可以将其替换为包含例如您的菜单的更丰富的值。您可以通过使用Play 2的actions composition机制来实现。
要做到这一点,您必须定义您的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>
使用Action组合允许您将模板所需的所有隐式值聚合到单个值中,但另一方面,您可能会失去一些灵活性...
使用Http.Context(Java)
由于Java没有类似于Scala的隐式机制,如果您想避免显式传递模板参数,则可以将它们存储在Http.Context
对象中,该对象仅存在于请求的持续时间内。该对象包含一个类型为Map<String,Object>
的args
值。
因此,您可以首先编写一个拦截器,如文档所述:
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");
}
}
静态方法只是一个简短的方式从当前上下文中检索菜单。然后将您的控制器注释为与
Menus
动作拦截器混合使用:
静态方法只是一个简洁的方式,用于从当前上下文中检索菜单。然后,在您的控制器上注解以与 Menus
动作拦截器混合使用:
@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>