Pregunta

En play1, normalmente obtengo todos los datos en acciones y los uso directamente en las vistas.Como no necesitamos declarar explícitamente los parámetros a la vista, esto es muy fácil.

Pero en play2, descubrí que tenemos que declarar todos los parámetros (incluido request) en la cabeza de vistas, será muy aburrido obtener todos los datos en acciones y pasarlos a vistas.

Por ejemplo, si necesito mostrar menús que se cargan desde la base de datos en la página principal, tengo que definirlo en 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>

Luego tengo que declararlo en cada subpágina:

@(menus: Seq[Menu])

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

Luego tengo que sacar los menús y pasarlos a ver en cada acción:

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 ahora es sólo un parámetro en main.scala.html, ¿y si son muchos?

Así que por fin decidí que todos Menu.findAll() directamente a la 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>

No sé si es bueno o recomendable, ¿hay alguna solución mejor para esto?

¿Fue útil?

Solución

En mi opinión, el hecho de que las plantillas se escriban estáticamente es en realidad una bien cosa:tiene la garantía de que llamar a su plantilla no fallará si se compila.

Sin embargo, de hecho agrega algunos textos estándar en los sitios de llamadas.Pero puedes reducirlo (sin perder las ventajas de la escritura estática).

En Scala, veo dos formas de lograrlo:mediante la composición de acciones o mediante el uso de parámetros implícitos.En Java sugiero usar el Http.Context.args map para almacenar valores útiles y recuperarlos de las plantillas sin tener que pasarlos explícitamente como parámetros de plantillas.

Usando parámetros implícitos

Colocar el menus parámetro al final de su main.scala.html parámetros de la plantilla y márquelo 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>

Ahora, si tiene plantillas que llaman a esta plantilla principal, puede tener la menus parámetro pasado implícitamente para usted al main plantilla por el compilador de Scala si también se declara como un parámetro implícito en estas plantillas:

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

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

Pero si desea que su controlador lo pase implícitamente, debe proporcionarlo como un valor implícito, disponible en el ámbito desde donde llama a la plantilla.Por ejemplo, puedes declarar el siguiente método en tu controlador:

implicit val menu: Seq[Menu] = Menu.findAll

Luego, en tus acciones podrás simplemente escribir lo siguiente:

def index = Action {
  Ok(views.html.index())
}

def index2 = Action {
  Ok(views.html.index2())
}

Puede encontrar más información sobre este enfoque en esta publicación de blog y en este ejemplo de código.

Actualizar:También se ha escrito una bonita publicación de blog que demuestra este patrón. aquí.

Usando la composición de acciones

En realidad, a menudo es útil pasar el RequestHeader valor a las plantillas (ver p.e. esta muestra).Esto no agrega tanto texto repetitivo al código de su controlador porque puede escribir fácilmente acciones que reciban un valor de solicitud implícito:

def index = Action { implicit request =>
  Ok(views.html.index()) // The `request` value is implicitly passed by the compiler
}

Entonces, dado que las plantillas a menudo reciben al menos este parámetro implícito, puede reemplazarlo con un valor más rico que contenga, por ejemplo.tus menús.Puedes hacerlo usando el composición de acciones Mecanismo del juego 2.

Para ello tienes que definir tu Context clase, envolviendo una solicitud subyacente:

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

Entonces puedes definir lo siguiente ActionWithMenu método:

def ActionWithMenu(f: Context => Result) = {
  Action { request =>
    f(Context(Menu.findAll, request))
  }
}

Que se puede utilizar así:

def index = ActionWithMenu { implicit context =>
  Ok(views.html.index())
}

Y puedes tomar el contexto como un parámetro implícito en tus plantillas.P.ej.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>

El uso de la composición de acciones le permite agregar todos los valores implícitos que sus plantillas requieren en un solo valor, pero por otro lado puede perder algo de flexibilidad...

Usando Http.Context (Java)

Dado que Java no tiene el mecanismo implícito de Scala o similar, si desea evitar pasar explícitamente parámetros de plantillas, una forma posible es almacenarlos en el Http.Context objeto que vive sólo durante la duración de una solicitud.Este objeto contiene un args valor de tipo Map<String, Object>.

Por lo tanto, puede comenzar escribiendo un interceptor, como se explica en la documentación:

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");
    }
}

El método estático es sólo una forma abreviada de recuperar los menús del contexto actual.Luego anota tu controlador para mezclarlo con el Menus interceptor de acción:

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

Finalmente, recupera el menus valor de sus plantillas de la siguiente manera:

@(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>

Otros consejos

La forma en que lo hago es simplemente crear un nuevo controlador para mi navegación/menú y llamarlo desde la vista.

Así podrás definir tu 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>
}

Entonces en mi vista principal puedo llamar a eso NavController:

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

Apoyo la respuesta de stian.Esta es una forma muy rápida de obtener resultados.

Acabo de migrar de Java+Play1.0 a Java+Play2.0 y las plantillas son la parte más difícil hasta ahora, y la mejor manera que encontré para implementar una plantilla base (para título, encabezado, etc.) es mediante el uso de Http. .Contexto.

Hay una sintaxis muy agradable que puedes lograr con etiquetas.

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

donde get.scala.html es:

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

y set.scala.html es:

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

significa que puedes escribir lo siguiente en cualquier plantilla

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

Por eso es muy legible y agradable.

Éste es el camino que elegí seguir.stian-buen consejo.Demuestra que es importante desplazarse hacia abajo para ver todas las respuestas.:)

Pasar variables HTML

Todavía no he descubierto cómo pasar variables HTML.

@(título:Cadena,contenido:Html)

sin embargo, sé cómo pasarlos como bloque.

@(título:Cadena)(contenido:Html)

por lo que es posible que desee reemplazar set.scala.html con

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

de esta manera puedes pasar bloques HTML así

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

EDITAR:Efecto secundario de mi implementación "establecida"

Un caso de uso común es la herencia de plantillas en Play.

Tienes base_template.html y luego tienes page_template.html que extiende base_template.html.

base_template.html podría verse así

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

mientras que la plantilla de página podría verse así

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

y luego tienes una página (supongamos login_page.html) que se parece a

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

@page_template()

Lo importante a tener en cuenta aquí es que configura "cuerpo" dos veces.Una vez en "login_page.html" y luego en "page_template.html".

Parece que esto desencadena un efecto secundario, siempre y cuando implementes set.scala.html como sugerí anteriormente.

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

ya que la página mostraría "cosas de inicio de sesión ..." dos veces porque put devuelve el valor que aparece la segunda vez que ingresamos la misma clave.(consulte poner firma en documentos de Java).

Scala proporciona una mejor manera de modificar el mapa.

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

que no causa este efecto secundario.

Si está utilizando Java y solo desea la forma más sencilla posible sin tener que escribir un interceptor y utilizar la anotación @With, también puede acceder al contexto HTTP directamente desde la plantilla.

P.ej.Si necesita una variable disponible desde una plantilla, puede agregarla al contexto HTTP con:

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

Luego puedes acceder a él desde la plantilla con:

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

Obviamente, si ensucia sus métodos con Http.Context.current().args.put("",""), es mejor que use un interceptor, pero para casos simples puede funcionar.

A partir de la respuesta de Stian, probé un enfoque diferente.Esto funciona para mí.

EN CÓDIGO JAVA

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

EN CABEZA DE PLANTILLA HTML

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

Y USAR COMO

@if(isOk) {
   <div>OK</div>
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top