Na minha opinião, o fato de que os modelos são estaticamente digitados é realmente uma coisa boa : você garante que a chamada do seu modelo não falhará se for compilada.
No entanto, ele realmente adiciona alguns clichês nos sites de chamada. Mas você pode reduzi-lo (sem perder as vantagens da digitação estática).
No Scala, vejo duas maneiras de alcançá-lo: através da composição de ações ou usando parâmetros implícitos. Em Java, sugiro usar o Http.Context.argsmapa para armazenar valores úteis e recuperá-los dos modelos sem ter que passar explicitamente como parâmetros de modelos.
Usando parâmetros implícitos
Coloque o menusparâmetro no final dos main.scala.htmlparâmetros do seu modelo e marque-o como "implícito":
@(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>
Agora, se você tiver modelos chamando esse modelo principal, poderá menuspassar o parâmetro implicitamente para o mainmodelo pelo compilador Scala, se ele também for declarado como parâmetro implícito nesses modelos:
@()(implicit menus: Seq[Menu])
@main("SubPage") {
...
}
Mas se você deseja que seja passado implicitamente do seu controlador, é necessário fornecê-lo como um valor implícito, disponível no escopo de onde você chama o modelo. Por exemplo, você pode declarar o seguinte método no seu controlador:
implicit val menu: Seq[Menu] = Menu.findAll
Em suas ações, você poderá escrever o seguinte:
def index = Action {
Ok(views.html.index())
}
def index2 = Action {
Ok(views.html.index2())
}
Você pode encontrar mais informações sobre essa abordagem nesta postagem do blog e neste exemplo de código .
Atualização : Um bom post de blog demonstrando esse padrão também foi escrito aqui .
Usando composição de ações
Na verdade, geralmente é útil passar o RequestHeadervalor para os modelos (veja, por exemplo, este exemplo ). Isso não adiciona muito clichê ao código do controlador, porque você pode escrever facilmente ações que recebem um valor implícito de solicitação:
def index = Action { implicit request =>
Ok(views.html.index()) // The `request` value is implicitly passed by the compiler
}
Portanto, como os modelos geralmente recebem pelo menos esse parâmetro implícito, você pode substituí-lo por um valor mais rico, contendo, por exemplo, seus menus. Você pode fazer isso usando o mecanismo de composição de ações do Play 2.
Para fazer isso, você precisa definir sua Contextclasse, agrupando uma solicitação subjacente:
case class Context(menus: Seq[Menu], request: Request[AnyContent])
extends WrappedRequest(request)
Então você pode definir o seguinte ActionWithMenumétodo:
def ActionWithMenu(f: Context => Result) = {
Action { request =>
f(Context(Menu.findAll, request))
}
}
Que pode ser usado assim:
def index = ActionWithMenu { implicit context =>
Ok(views.html.index())
}
E você pode considerar o contexto como um parâmetro implícito em seus modelos. Por exemplo, para 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>
O uso da composição de ações permite agregar todos os valores implícitos que seus modelos exigem em um único valor, mas por outro lado, você pode perder alguma flexibilidade…
Usando Http.Context (Java)
Como o Java não possui o mecanismo implícito do Scala ou similar, se você deseja evitar passar explicitamente os parâmetros dos modelos, uma maneira possível é armazená-los no Http.Contextobjeto que vive apenas pela duração de uma solicitação. Este objecto contém um argsvalor de tipo Map<String, Object>.
Assim, você pode começar escrevendo um interceptador, conforme explicado na documentação :
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");
}
}
O método estático é apenas uma abreviação para recuperar os menus do contexto atual. Em seguida, anote seu controlador para ser misturado com o Menusinterceptor de ação:
@With(Menus.class)
public class Application extends Controller {
// …
}
Por fim, recupere o menusvalor dos seus modelos da seguinte maneira:
@(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>