À 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.argscarte 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 menusparamètre à la fin de vos main.scala.htmlparamè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 menusparamètre implicitement pour vous au mainmodè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 RequestHeadervaleur 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 Contextclasse 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 ActionWithMenumé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.Contextobjet qui ne vit que pendant la durée d'une requête. Cet objet contient une argsvaleur 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' Menusintercepteur d'action:
@With(Menus.class)
public class Application extends Controller {
// …
}
Enfin, récupérez la menusvaleur 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>