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  
@for(menu <- context.menus) { @menu.name }
@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  
@for(menu <- Menus.current()) { @menu.name }
@content

A maneira que eu faço isso é apenas criar um novo controlador para minha navegação / menu e chamá-lo da visão

Então você pode definir seu NavController :

 object NavController extends Controller { private val navList = "Home" :: "About" :: "Contact" :: Nil def nav = views.html.nav(navList) } 

nav.scala.html

 @(navLinks: Seq[String]) @for(nav <- navLinks) { @nav } 

Então, na minha visão principal, posso chamar esse NavController :

 @(title: String)(content: Html)    @title   @NavController.nav @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
}
Intereting Posts