Como evitar a passagem de parâmetros em todos os lugares no play2?

No play1, geralmente, obtenho todos os dados em ações, uso-os diretamente nas visualizações. Como não precisamos declarar explicitamente os parâmetros, isso é muito fácil.

Mas no play2, descobri que temos que declarar todos os parâmetros (incluindo request ) na cabeça de views, será muito chato obter todos os dados em ações e passá-los para views.

Por exemplo, se eu precisar exibir menus que são carregados do database na primeira página, tenho que defini-lo em main.scala.html :

 @(title: String, menus: Seq[Menu])(content: Html) @title  
@for(menu<-menus) { @menu.name }
@content

Então eu tenho que declarar em cada subpágina:

 @(menus: Seq[Menu]) @main("SubPage", menus) { ... } 

Então eu tenho que pegar os menus e passá-lo para ver em cada ação:

 def index = Action { val menus = Menu.findAll() Ok(views.html.index(menus)) } def index2 = Action { val menus = Menu.findAll() Ok(views.html.index2(menus)) } def index3 = Action { val menus = Menu.findAll() Ok(views.html.index(menus3)) } 

Por enquanto é apenas um parâmetro em main.scala.html , e se houver muitos?

Então, finalmente, decidi ver todos os Menu.findAll() diretamente:

 @(title: String)(content: Html) @title  
@for(menu<-Menu.findAll()) { @menu.name }
@content

Não sei se é bom ou recomendado, existe alguma solução melhor para isso?

    Na minha opinião, o fato de os modelos serem estaticamente typescripts é realmente uma coisa boa : você tem a garantia de que chamar seu modelo não falhará se for compilado.

    No entanto, isso realmente adiciona algum clichê nos sites de chamada. Mas você pode reduzi-lo (sem perder as vantagens da tipagem 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 mapa Http.Context.args para armazenar valores úteis e recuperá-los dos modelos sem ter que passar explicitamente como parâmetros de modelos.

    Usando parameters Implícitos

    Coloque o parâmetro menus no final dos seus parâmetros main.scala.html e marque-o como “implícito”:

     @(title: String)(content: Html)(implicit menus: Seq[Menu])  @title  
    @for(menu< -menus) { @menu.name }
    @content

    Agora, se você tiver modelos chamando esse modelo principal, poderá ter o parâmetro menus implicitamente passado para o modelo main pelo compilador Scala se ele também for declarado como um parâmetro implícito nesses modelos:

     @()(implicit menus: Seq[Menu]) @main("SubPage") { ... } 

    Mas se você quiser que isso seja passado implicitamente do seu controlador, você precisa 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 

    Então, em suas ações, você poderá simplesmente 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 neste post de blog e neste exemplo de código .

    Atualização : Um bom post no blog demonstrando esse padrão também foi escrito aqui .

    Usando a composição de ações

    Na verdade, muitas vezes é útil passar o valor de RequestHeader para os modelos (ver, por exemplo, este exemplo ). Isso não adiciona muito clichê ao código do controlador, pois é possível gravar facilmente ações que recebem um valor de solicitação implícito:

     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ê poderia 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 class Context , envolvendo uma solicitação subjacente:

     case class Context(menus: Seq[Menu], request: Request[AnyContent]) extends WrappedRequest(request) 

    Em seguida, você pode definir o seguinte método ActionWithMenu :

     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 pegar o contexto como um parâmetro implícito em seus modelos. Por exemplo, para main.scala.html :

     @(title: String)(content: Html)(implicit context: Context) @title   @content   

    Usar a 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 o Http.Context (Java)

    Como o Java não possui o mecanismo implícito do Scala ou similar, se você quiser evitar explicitamente passar parâmetros de modelos, uma maneira possível é armazená-los no object Http.Context que vive apenas pela duração de uma solicitação. Este object contém um valor de args do tipo Map .

    Assim, você pode começar escrevendo um interceptor, 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 current() { return (List)Http.Context.current().args.get("menus"); } } 

    O método estático é apenas um atalho para recuperar os menus do contexto atual. Em seguida, anote seu controlador para ser misturado com o interceptor de ação Menus :

     @With(Menus.class) public class Application extends Controller { // … } 

    Finalmente, recupere o valor dos menus dos seus modelos da seguinte maneira:

     @(title: String)(content: Html)  @title   @content   

    Se você estiver usando Java e quiser apenas a maneira mais simples possível sem precisar gravar um interceptor e usar a anotação @With, também poderá acessar o contexto HTTP diretamente do modelo.

    Por exemplo, se você precisar de uma variável disponível a partir de um modelo, poderá adicioná-la ao contexto HTTP com:

     Http.Context.current().args.put("menus", menus) 

    Você pode acessá-lo a partir do modelo com:

     @Http.Context.current().args.get("menus").asInstanceOf[List] 

    Obviamente, se você ninhar seus methods com Http.Context.current (). Args.put (“”, “”), é melhor usar um interceptador, mas, em casos simples, isso pode funcionar.

    Eu apoio a resposta de stian. Esta é uma maneira muito rápida de obter resultados.

    Acabei de migrar de Java + Play1.0 para Java + Play2.0 e os modelos são a parte mais difícil até agora, e a melhor maneira que encontrei para implementar um modelo base (para título, header, etc.) é usando o Http .Contexto.

    Existe uma syntax muito legal que você pode conseguir com tags.

     views | \--- tags | \------context | \-----get.scala.html \-----set.scala.html 

    onde get.scala.html é:

     @(key:String) @{play.mvc.Http.Context.current().args.get(key)} 

    e set.scala.html é:

     @(key:String,value:AnyRef) @{play.mvc.Http.Context.current().args.put(key,value)} 

    significa que você pode escrever o seguinte em qualquer modelo

     @import tags._ @contest.set("myKey","myValue") @context.get("myKey") 

    Por isso, é muito legível e agradável.

    É assim que escolhi ir. stian – bom conselho. Prova é importante rolar para baixo para ver todas as respostas. 🙂

    Passando variables ​​HTML

    Eu ainda não descobri como passar variables ​​HTML.

    @ (title: String, conteúdo: Html)

    no entanto, sei como passá-los como bloco.

    @ (title: String) (conteúdo: Html)

    então você pode querer replace set.scala.html com

     @(key:String)(value:AnyRef) @{play.mvc.Http.Context.current().args.put(key,value)} 

    Desta forma, você pode passar blocos HTML como assim

     @context.set("head"){  @callSomeFun(withParameter) } 

    EDIT: efeito colateral com meu “conjunto” implementação

    Um caso de uso comum é a inheritance de modelos no Play.

    Você tem um base_template.html e, em seguida, você tem page_template.html que estende base_template.html.

    base_template.html pode parecer algo como

        @context.get("title")   @context.get("body")   

    enquanto modelo de página pode parecer algo como

     @context.set("body){ some page common context here.. @context.get("body") } @base_template() 

    e então você tem uma página (vamos assumir login_page.html) que parece

     @context.set("title"){login} @context.set("body"){ login stuff.. } @page_template() 

    O importante a notar aqui é que você define “corpo” duas vezes. Uma vez em “login_page.html” e depois em “page_template.html”.

    Parece que isso desencadeia um efeito colateral, contanto que você implemente set.scala.html como sugeri acima.

     @{play.mvc.Http.Context.current().put(key,value)} 

    como a página mostraria “login stuff …” duas vezes porque put retorna o valor que aparece na segunda vez que colocamos a mesma chave. (veja colocar assinatura em java docs).

    scala fornece uma maneira melhor de modificar o mapa

     @{play.mvc.Http.Context.current().args(key)=value} 

    que não causa esse efeito colateral.

    Da resposta de Stian, tentei uma abordagem diferente. Isso funciona para mim.

    NO CÓDIGO JAVA

     import play.mvc.Http.Context; Context.current().args.put("isRegisterDone", isRegisterDone); 

    NA CABEÇA DO MODELO DE HTML

     @import Http.Context @isOk = @{ Option(Context.current().args.get("isOk")).getOrElse(false).asInstanceOf[Boolean] } 

    E usar como

     @if(isOk) { 
    OK
    }