Moim zdaniem fakt, że szablony są typowane statycznie, jest w rzeczywistości dobrą rzeczą: masz gwarancję, że wywołanie szablonu nie zakończy się niepowodzeniem, jeśli się skompiluje.
Jednak rzeczywiście dodaje pewne standardowe elementy do stron wywołujących. Ale możesz to zmniejszyć (bez utraty zalet pisania statycznego).
W Scali widzę dwa sposoby osiągnięcia tego: poprzez kompozycję akcji lub użycie niejawnych parametrów. W Javie sugeruję użycie Http.Context.argsmapy do przechowywania przydatnych wartości i pobierania ich z szablonów bez konieczności jawnego przekazywania jako parametrów szablonów.
Używanie niejawnych parametrów
Umieść menusparametr na końcu main.scala.htmlparametrów szablonu i oznacz go jako „niejawny”:
@(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>
Teraz, jeśli masz szablony wywołujące ten główny szablon, możesz mieć menusparametr niejawnie przekazany do mainszablonu przez kompilator Scala, jeśli jest on również zadeklarowany jako niejawny parametr w tych szablonach:
@()(implicit menus: Seq[Menu])
@main("SubPage") {
...
}
Ale jeśli chcesz, aby była ona niejawnie przekazywana z kontrolera, musisz podać ją jako niejawną wartość, dostępną w zakresie, z którego wywołujesz szablon. Na przykład możesz zadeklarować w kontrolerze następującą metodę:
implicit val menu: Seq[Menu] = Menu.findAll
Następnie w swoich działaniach będziesz mógł po prostu napisać:
def index = Action {
Ok(views.html.index())
}
def index2 = Action {
Ok(views.html.index2())
}
Więcej informacji na temat tego podejścia można znaleźć w tym poście na blogu oraz w tym przykładzie kodu .
Aktualizacja : ładny wpis na blogu demonstrujący ten wzór został również napisany tutaj .
Używanie kompozycji akcji
W rzeczywistości często przydatne jest przekazywanie RequestHeaderwartości do szablonów (patrz np. Ten przykład ). Nie dodaje to zbyt wiele schematu do kodu kontrolera, ponieważ można łatwo pisać akcje otrzymujące niejawną wartość żądania:
def index = Action { implicit request =>
Ok(views.html.index()) // The `request` value is implicitly passed by the compiler
}
Tak więc, ponieważ szablony często otrzymują przynajmniej ten niejawny parametr, możesz zastąpić go bogatszą wartością zawierającą np. Twoje menu. Możesz to zrobić za pomocą mechanizmu kompozycji akcji Play 2.
Aby to zrobić, musisz zdefiniować swoją Contextklasę, opakowując podstawowe żądanie:
case class Context(menus: Seq[Menu], request: Request[AnyContent])
extends WrappedRequest(request)
Następnie możesz zdefiniować następującą ActionWithMenumetodę:
def ActionWithMenu(f: Context => Result) = {
Action { request =>
f(Context(Menu.findAll, request))
}
}
Które można wykorzystać w ten sposób:
def index = ActionWithMenu { implicit context =>
Ok(views.html.index())
}
I możesz wziąć kontekst jako niejawny parametr w swoich szablonach. Np. Dla 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>
Korzystanie z kompozycji akcji umożliwia agregowanie wszystkich niejawnych wartości wymaganych przez szablony w jedną wartość, ale z drugiej strony możesz stracić pewną elastyczność…
Korzystanie z Http.Context (Java)
Ponieważ Java nie ma mechanizmu implicits Scala lub podobnego, jeśli chcesz uniknąć jawnego przekazywania parametrów szablonów, możliwym sposobem jest przechowywanie ich w Http.Contextobiekcie, który istnieje tylko na czas trwania żądania. Ten obiekt zawiera argswartość typu Map<String, Object>.
Dlatego możesz zacząć od napisania przechwytywacza, jak wyjaśniono w dokumentacji :
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");
}
}
Metoda statyczna to tylko skrót do pobierania menu z bieżącego kontekstu. Następnie dodaj adnotację do kontrolera, który ma zostać zmieszany z Menusprzechwytywaczem akcji:
@With(Menus.class)
public class Application extends Controller {
// …
}
Na koniec pobierz menuswartość ze swoich szablonów w następujący sposób:
@(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>