A mio avviso, il fatto che i modelli siano tipizzati staticamente è in realtà una buona cosa: sei sicuro che la chiamata al tuo modello non fallirà se si compila.
Tuttavia, in effetti aggiunge un po 'di boilerplate sui siti di chiamata. Ma puoi ridurlo (senza perdere i vantaggi della digitazione statica).
In Scala, vedo due modi per raggiungerlo: attraverso la composizione di azioni o usando parametri impliciti. In Java suggerisco di utilizzare la Http.Context.args
mappa per memorizzare valori utili e recuperarli dai modelli senza dover passare esplicitamente come parametri dei modelli.
Utilizzando parametri impliciti
Posiziona il menus
parametro alla fine dei main.scala.html
parametri del modello e contrassegnalo come "implicito":
@(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>
Ora se hai modelli che chiamano questo modello principale, puoi avere il menus
parametro implicitamente passato al main
compilatore Scala dal tuo compilatore Scala se è dichiarato come parametro implicito anche in questi modelli:
@()(implicit menus: Seq[Menu])
@main("SubPage") {
...
}
Ma se si desidera che venga passato in modo implicito dal controller, è necessario fornirlo come valore implicito, disponibile nell'ambito da cui si chiama il modello. Ad esempio, è possibile dichiarare il seguente metodo nel controller:
implicit val menu: Seq[Menu] = Menu.findAll
Quindi nelle tue azioni sarai in grado di scrivere solo quanto segue:
def index = Action {
Ok(views.html.index())
}
def index2 = Action {
Ok(views.html.index2())
}
Puoi trovare ulteriori informazioni su questo approccio in questo post di blog e in questo esempio di codice .
Aggiornamento : qui è stato scritto anche un bel post sul blog che dimostra questo schema .
Usando la composizione delle azioni
In realtà, è spesso utile passare il RequestHeader
valore ai template (vedere ad esempio questo esempio ). Questo non aggiunge così tanto boilerplate al codice del tuo controller perché puoi facilmente scrivere azioni che ricevono un valore di richiesta implicito:
def index = Action { implicit request =>
Ok(views.html.index()) // The `request` value is implicitly passed by the compiler
}
Pertanto, poiché i modelli spesso ricevono almeno questo parametro implicito, è possibile sostituirlo con un valore più ricco contenente, ad esempio, i menu. Puoi farlo usando il meccanismo di composizione delle azioni di Play 2.
Per fare ciò devi definire la tua Context
classe, avvolgendo una richiesta sottostante:
case class Context(menus: Seq[Menu], request: Request[AnyContent])
extends WrappedRequest(request)
Quindi è possibile definire il seguente ActionWithMenu
metodo:
def ActionWithMenu(f: Context => Result) = {
Action { request =>
f(Context(Menu.findAll, request))
}
}
Quale può essere usato in questo modo:
def index = ActionWithMenu { implicit context =>
Ok(views.html.index())
}
E puoi prendere il contesto come parametro implicito nei tuoi modelli. Ad esempio per 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>
L'uso della composizione delle azioni ti consente di aggregare tutti i valori impliciti richiesti dai tuoi modelli in un singolo valore, ma d'altra parte puoi perdere un po 'di flessibilità ...
Utilizzo di Http.Context (Java)
Poiché Java non ha il meccanismo implicito di Scala o simili, se si desidera evitare di passare esplicitamente i parametri dei modelli, un modo possibile è quello di memorizzarli Http.Context
nell'oggetto che vive solo per la durata di una richiesta. Questo oggetto contiene un args
valore di tipo Map<String, Object>
.
Quindi, puoi iniziare scrivendo un intercettore, come spiegato nella documentazione :
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");
}
}
Il metodo statico è solo una scorciatoia per recuperare i menu dal contesto corrente. Quindi annota il controller da mescolare con l' Menus
interceptor di azioni:
@With(Menus.class)
public class Application extends Controller {
// …
}
Infine, recupera il menus
valore dai tuoi modelli come segue:
@(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>