На мой взгляд, тот факт, что шаблоны статически типизированы, на самом деле хорошая вещь: вы гарантированно не вызовете ваш шаблон, если он скомпилируется.
Тем не менее, это действительно добавляет некоторые шаблоны на вызывающих сайтах. Но вы можете уменьшить его (без потери преимуществ статической печати).
В 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") {
...
}
Но если вы хотите, чтобы он неявно передавался из вашего контроллера, вам нужно предоставить его как неявное значение, доступное в области, из которой вы вызываете шаблон. Например, вы можете объявить следующий метод в вашем контроллере:
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()) // The `request` value is implicitly passed by the compiler
}
Итак, поскольку шаблоны часто получают по крайней мере этот неявный параметр, вы можете заменить его более богатым значением, содержащим, например, твои меню. Вы можете сделать это, используя механизм действия механизма 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>
Использование композиции действий позволяет объединять все неявные значения, которые требуются вашим шаблонам, в одно значение, но, с другой стороны, вы можете потерять некоторую гибкость ...
Использование Http.Context (Java)
Поскольку в Java нет механизма влияния Scala или чего-либо подобного, если вы хотите избежать явной передачи параметров шаблонов, возможный способ - сохранить их в объекте Http.Context
, который существует только на время запроса. Этот объект содержит значение args
типа Map<String, Object>
.
Таким образом, вы можете начать с написания перехватчика, как описано в документации :
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
:
@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>