À mon avis, le fait que les modèles soient typés statiquement est en fait une bonne chose: vous êtes assuré que l'appel de votre modèle n'échouera pas s'il se compile.
Cependant, il ajoute en effet un passe-partout sur les sites d'appel. Mais vous pouvez le réduire (sans perdre les avantages de la saisie statique).
Dans Scala, je vois deux façons d'y parvenir: via la composition d'actions ou en utilisant des paramètres implicites. En Java, je suggère d'utiliser la Http.Context.args
carte pour stocker des valeurs utiles et les récupérer à partir des modèles sans avoir à passer explicitement comme paramètres de modèles.
Utilisation de paramètres implicites
Placez le menus
paramètre à la fin de vos main.scala.html
paramètres de modèle et marquez-le comme «implicite»:
@(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>
Maintenant, si vous avez des modèles appelant ce modèle principal, vous pouvez faire passer le menus
paramètre implicitement pour vous au main
modèle par le compilateur Scala s'il est également déclaré en tant que paramètre implicite dans ces modèles:
@()(implicit menus: Seq[Menu])
@main("SubPage") {
...
}
Mais si vous voulez le faire passer implicitement de votre contrôleur, vous devez le fournir en tant que valeur implicite, disponible dans la portée à partir de laquelle vous appelez le modèle. Par exemple, vous pouvez déclarer la méthode suivante dans votre contrôleur:
implicit val menu: Seq[Menu] = Menu.findAll
Ensuite, dans vos actions, vous pourrez simplement écrire ce qui suit:
def index = Action {
Ok(views.html.index())
}
def index2 = Action {
Ok(views.html.index2())
}
Vous pouvez trouver plus d'informations sur cette approche dans cet article de blog et dans cet exemple de code .
Mise à jour : Un joli article de blog démontrant ce modèle a également été écrit ici .
Utilisation de la composition d'actions
En fait, il est souvent utile de transmettre la RequestHeader
valeur aux modèles (voir par exemple cet exemple ). Cela n'ajoute pas tellement de passe-partout au code de votre contrôleur car vous pouvez facilement écrire des actions recevant une valeur de demande implicite:
def index = Action { implicit request =>
Ok(views.html.index()) // The `request` value is implicitly passed by the compiler
}
Ainsi, étant donné que les modèles reçoivent souvent au moins ce paramètre implicite, vous pouvez le remplacer par une valeur plus riche contenant par exemple vos menus. Vous pouvez le faire en utilisant le mécanisme de composition d'actions de Play 2.
Pour ce faire, vous devez définir votre Context
classe en encapsulant une requête sous-jacente:
case class Context(menus: Seq[Menu], request: Request[AnyContent])
extends WrappedRequest(request)
Ensuite, vous pouvez définir la ActionWithMenu
méthode suivante :
def ActionWithMenu(f: Context => Result) = {
Action { request =>
f(Context(Menu.findAll, request))
}
}
Qui peut être utilisé comme ceci:
def index = ActionWithMenu { implicit context =>
Ok(views.html.index())
}
Et vous pouvez prendre le contexte comme paramètre implicite dans vos modèles. Par exemple pour 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'utilisation de la composition d'actions vous permet d'agréger toutes les valeurs implicites dont vos modèles ont besoin en une seule valeur, mais d'un autre côté, vous pouvez perdre une certaine flexibilité ...
Utilisation de Http.Context (Java)
Puisque Java n'a pas de mécanisme d'implication de Scala ou similaire, si vous voulez éviter de passer explicitement les paramètres des modèles, un moyen possible est de les stocker dans l' Http.Context
objet qui ne vit que pendant la durée d'une requête. Cet objet contient une args
valeur de type Map<String, Object>
.
Ainsi, vous pouvez commencer par écrire un intercepteur, comme expliqué dans la documentation :
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");
}
}
La méthode statique n'est qu'un raccourci pour récupérer les menus du contexte actuel. Ensuite, annotez votre contrôleur pour qu'il soit mélangé avec l' Menus
intercepteur d'action:
@With(Menus.class)
public class Application extends Controller {
// …
}
Enfin, récupérez la menus
valeur de vos modèles comme suit:
@(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>