На мой взгляд, тот факт, что шаблоны статически типизированы, на самом деле хорошо : вы гарантируете, что вызов вашего шаблона не завершится ошибкой, если он компилируется.
Однако он действительно добавляет некоторый шаблон на вызывающие сайты. Но вы можете уменьшить его (не теряя преимуществ статической печати).
В 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>