Meiner Meinung nach ist die Tatsache, dass Vorlagen statisch typisiert sind, eine gute Sache: Sie können sicher sein, dass das Aufrufen Ihrer Vorlage beim Kompilieren nicht fehlschlägt.
Es wird jedoch tatsächlich ein Boilerplate auf den aufrufenden Sites hinzugefügt. Aber man kann es reduzieren (ohne statische Typisierung Vorteile zu verlieren).
In Scala sehe ich zwei Möglichkeiten, dies zu erreichen: durch Zusammenstellung von Aktionen oder durch Verwendung impliziter Parameter. In Java empfehle ich, die Http.Context.args
Map zu verwenden, um nützliche Werte zu speichern und aus den Vorlagen abzurufen, ohne explizit als Vorlagenparameter übergeben zu müssen.
Implizite Parameter verwenden
Platzieren Sie den menus
Parameter am Ende Ihrer main.scala.html
Vorlagenparameter und markieren Sie ihn als "implizit":
@(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>
Wenn Sie nun Vorlagen haben, die diese Hauptvorlage aufrufen, können Sie den menus
Parameter main
vom Scala-Compiler implizit an die Vorlage übergeben lassen, wenn er auch in diesen Vorlagen als impliziter Parameter deklariert ist:
@()(implicit menus: Seq[Menu])
@main("SubPage") {
...
}
Wenn Sie jedoch möchten, dass es implizit von Ihrem Controller übergeben wird, müssen Sie es als impliziten Wert angeben, der in dem Bereich verfügbar ist, in dem Sie die Vorlage aufrufen. Sie können beispielsweise die folgende Methode in Ihrem Controller deklarieren:
implicit val menu: Seq[Menu] = Menu.findAll
Dann können Sie in Ihren Aktionen einfach Folgendes schreiben:
def index = Action {
Ok(views.html.index())
}
def index2 = Action {
Ok(views.html.index2())
}
Weitere Informationen zu diesem Ansatz finden Sie in diesem Blogbeitrag und in diesem Codebeispiel .
Update : Ein schöner Blogeintrag dieses Muster demonstriert hat auch geschrieben worden hier .
Verwenden der Aktionskomposition
Tatsächlich ist es oft nützlich, den RequestHeader
Wert an die Vorlagen zu übergeben (siehe z . B. dieses Beispiel ). Dadurch wird Ihrem Controller-Code nicht so viel Boilerplate hinzugefügt, da Sie problemlos Aktionen schreiben können, die einen impliziten Anforderungswert erhalten:
def index = Action { implicit request =>
Ok(views.html.index()) // The `request` value is implicitly passed by the compiler
}
Da Vorlagen häufig mindestens diesen impliziten Parameter erhalten, können Sie ihn durch einen umfangreicheren Wert ersetzen, der z. B. Ihre Menüs enthält. Sie können das tun , indem Sie die Verwendung von Aktionen Zusammensetzung Mechanismus der Wiedergabe 2.
Dazu müssen Sie Ihre Context
Klasse definieren und eine zugrunde liegende Anforderung umschließen:
case class Context(menus: Seq[Menu], request: Request[AnyContent])
extends WrappedRequest(request)
Dann können Sie die folgende ActionWithMenu
Methode definieren :
def ActionWithMenu(f: Context => Result) = {
Action { request =>
f(Context(Menu.findAll, request))
}
}
Welches kann so verwendet werden:
def index = ActionWithMenu { implicit context =>
Ok(views.html.index())
}
Und Sie können den Kontext als impliziten Parameter in Ihren Vorlagen verwenden. ZB für 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>
Durch die Verwendung der Aktionskomposition können Sie alle impliziten Werte, die Ihre Vorlagen benötigen, zu einem einzigen Wert zusammenfassen. Andererseits können Sie an Flexibilität verlieren.
Verwenden von Http.Context (Java)
Da Java nicht über den impliziten Mechanismus von Scala oder ähnliches verfügt, können Sie Vorlagenparameter möglicherweise in dem Http.Context
Objekt speichern, das nur für die Dauer einer Anforderung gültig ist, wenn Sie vermeiden möchten, Vorlagenparameter explizit zu übergeben . Dieses Objekt enthält einen args
Wert vom Typ Map<String, Object>
.
Sie können also zunächst einen Interceptor schreiben, wie in der Dokumentation erläutert :
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");
}
}
Die statische Methode ist nur eine Abkürzung, um die Menüs aus dem aktuellen Kontext abzurufen. Kommentieren Sie dann Ihren Controller mit dem Menus
Action Interceptor:
@With(Menus.class)
public class Application extends Controller {
// …
}
Rufen Sie abschließend den menus
Wert wie folgt aus Ihren Vorlagen ab:
@(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>