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