Pergunta

No play1, costumo obter todos os dados em ações, utilizá-los diretamente nas visualizações.Como não precisamos declarar explicitamente os parâmetros à vista, isso é muito fácil.

Mas no play2 descobri que precisamos declarar todos os parâmetros(incluindo request) no head das views, será muito chato pegar todos os dados nas ações e passá-los para as views.

Por exemplo, se eu precisar exibir menus carregados do banco de dados na primeira página, tenho que defini-lo em main.scala.html:

@(title: String, menus: Seq[Menu])(content: Html)    

<html><head><title>@title</title></head>
<body>
    <div>
    @for(menu<-menus) {
       <a href="#">@menu.name</a>
    }
    </div>
    @content
</body></html>

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

@(menus: Seq[Menu])

@main("SubPage", menus) {
   ...
}

Depois tenho que pegar os menus e passá-los para visualização em todas as ações:

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 que todos Menu.findAll() diretamente à vista:

@(title: String)(content: Html)    

<html><head><title>@title</title></head>
<body>
    <div>
    @for(menu<-Menu.findAll()) {
       <a href="#">@menu.name</a>
    }
    </div>
    @content
</body></html>

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

Foi útil?

Solução

Na minha opinião, o fato de os modelos serem digitados estaticamente é na verdade um bom coisa:você tem a garantia de que a chamada do seu modelo não falhará se ele for compilado.

No entanto, ele realmente adiciona alguns padrões aos sites de chamada.Mas você pode reduzi-lo (sem perder as vantagens da digitação estática).

No Scala, vejo duas maneiras de conseguir isso:através da composição de ações ou usando parâmetros implícitos.Em Java eu ​​sugiro usar o Http.Context.args map 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 menus parâmetro no final do seu main.scala.html parâmetros do 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 este modelo principal, poderá ter o menus parâmetro passado implicitamente para você para o main template pelo compilador Scala se também for declarado como um parâmetro implícito nestes templates:

@()(implicit menus: Seq[Menu])

@main("SubPage") {
  ...
}

Mas se você quiser que ele 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 em 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 em esta postagem do blog e em este exemplo de código.

Atualizar:Uma bela postagem no blog demonstrando esse padrão também foi escrita aqui.

Usando composição de ações

Na verdade, muitas vezes é útil passar o RequestHeader valor para os modelos (veja, por exemplo, esta amostra).Isso não adiciona muito padrão ao código do seu controlador porque você pode facilmente escrever ações recebendo 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ê pode substituí-lo por um valor mais rico contendo, por exemplo.seus cardápios.Você pode fazer isso usando o composição de ações mecanismo da peça 2.

Para fazer isso você tem que definir seu Context classe, agrupando uma solicitação subjacente:

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

Então você pode definir o seguinte ActionWithMenu mé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>

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

Como Java não possui o mecanismo implícito do Scala ou similar, se você quiser evitar passar explicitamente os parâmetros dos templates, uma maneira possível é armazená-los no Http.Context objeto que vive apenas durante uma solicitação.Este objeto contém um args valor do tipo Map<String, Object>.

Assim, você pode começar escrevendo um interceptador, conforme explicado em a 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 um atalho para recuperar os menus do contexto atual.Em seguida, anote seu controlador para ser mixado com o Menus interceptador de ação:

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

Por fim, recupere o menus valor de 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>

Outras dicas

A maneira como faço isso é apenas criar um novo controlador para minha navegação/menu e chamá-lo a partir da visualização

Assim 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) {
  <a href="#">@nav</a>
}

Então, na minha visualização principal, posso chamar isso NavController:

@(title: String)(content: Html)
<!DOCTYPE html>
<html>
  <head>
    <title>@title</title>
  </head>
  <body>
     @NavController.nav
     @content
  </body>
</html>

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

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

Existe uma sintaxe muito boa que você pode conseguir com tags.

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

onde get.scala.html está:

@(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._
@context.set("myKey","myValue")
@context.get("myKey")

Portanto, é muito legível e agradável.

Este é o caminho que escolhi seguir.stian - bom conselho.Prova que é importante rolar para baixo para ver todas as respostas.:)

Passando variáveis ​​HTML

Ainda não descobri como passar variáveis ​​HTML.

@(título:String,conteúdo:Html)

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

@(título:String)(conteúdo:Html)

então você pode querer substituir set.scala.html por

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

desta forma você pode passar blocos HTML assim

@context.set("head"){ 
     <meta description="something here"/> 
     @callSomeFunction(withParameter)
}

EDITAR:Efeito colateral com minha implementação "Set"

Um caso de uso comum é herança de modelo no Play.

Você tem um base_template.html e depois um page_template.html que estende base_template.html.

base_template.html pode ser parecido com

<html> 
    <head>
        <title> @context.get("title")</title>
    </head>
    <body>
       @context.get("body")
    </body>
</html>

enquanto o modelo de página pode ser parecido com

@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 se parece com

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

@page_template()

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

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

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

já que a página mostraria "coisas de login..." duas vezes porque put retorna o valor que aparece na segunda vez que colocamos a mesma chave.(veja colocar assinatura em documentos Java).

scala fornece uma maneira melhor de modificar o mapa

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

o que não causa esse efeito colateral.

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

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

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

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

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

Obviamente, se você desarrumar seus métodos com Http.Context.current().args.put("","") é melhor usar um interceptador, mas para casos simples ele pode resolver o problema.

Pela 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);

NO CABEÇA DO MODELO HTML

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

E USE LIKE

@if(isOk) {
   <div>OK</div>
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top