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.args
mapy 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ść menus
parametr na końcu main.scala.html
parametró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ć menus
parametr niejawnie przekazany do main
szablonu 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 RequestHeader
wartoś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ą Context
klasę, opakowując podstawowe żądanie:
case class Context(menus: Seq[Menu], request: Request[AnyContent])
extends WrappedRequest(request)
Następnie możesz zdefiniować następującą ActionWithMenu
metodę:
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.Context
obiekcie, który istnieje tylko na czas trwania żądania. Ten obiekt zawiera args
wartość 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 Menus
przechwytywaczem akcji:
@With(Menus.class)
public class Application extends Controller {
// …
}
Na koniec pobierz menus
wartość 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>