Frage

In Play1 erhalte ich normalerweise alle Daten in Aktionen und verwende sie direkt in Ansichten.Da wir die Parameter in view nicht explizit deklarieren müssen, ist dies sehr einfach.

Aber in Play2 habe ich festgestellt, dass wir alle Parameter deklarieren müssen (einschließlich request) im Kopf der Ansichten, wird es sehr langweilig sein, alle Daten in Aktionen abzurufen und sie an Ansichten zu übergeben.

Wenn ich beispielsweise Menüs, die aus der Datenbank geladen werden, auf der Startseite anzeigen muss, muss ich dies in definieren 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>

Dann muss ich es auf jeder Unterseite deklarieren:

@(menus: Seq[Menu])

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

Dann muss ich die Menüs abrufen und sie in jeder Aktion zur Ansicht übergeben:

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

Im Moment ist es nur ein Parameter in main.scala.html, was ist, wenn es viele sind?

Also entschied ich mich schließlich für alles Menu.findAll() direkt im Blick:

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

Ich weiß nicht, ob es gut oder empfehlenswert ist. Gibt es dafür eine bessere Lösung?

War es hilfreich?

Lösung

Meiner Meinung nach ist die Tatsache, dass Vorlagen statisch typisiert sind, tatsächlich ein Problem Gut Ding:Sie können sicher sein, dass der Aufruf Ihrer Vorlage beim Kompilieren nicht fehlschlägt.

Es fügt jedoch tatsächlich einige Standardfunktionen auf den aufrufenden Websites hinzu.Aber Sie können es reduzieren (ohne die Vorteile der statischen Typisierung zu verlieren).

In Scala sehe ich zwei Möglichkeiten, dies zu erreichen:durch die Zusammensetzung von Aktionen oder durch die Verwendung impliziter Parameter.In Java empfehle ich die Verwendung von Http.Context.args Map, um nützliche Werte zu speichern und aus den Vorlagen abzurufen, ohne explizit Parameter als Vorlagen übergeben zu müssen.

Verwendung impliziter Parameter

Setze das menus Parameter am Ende Ihres main.scala.html Vorlagenparameter und markieren Sie es als „implizit“:

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

Wenn Sie nun über Vorlagen verfügen, die diese Hauptvorlage aufrufen, können Sie diese verwenden menus Parameter, der implizit für Sie an übergeben wird main Vorlage durch den Scala-Compiler, wenn es auch in diesen Vorlagen als impliziter Parameter deklariert ist:

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

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

Wenn Sie es jedoch implizit von Ihrem Controller übergeben lassen möchten, müssen Sie es als impliziten Wert bereitstellen, der in dem Bereich verfügbar ist, von dem aus Sie die Vorlage aufrufen.Sie können beispielsweise die folgende Methode in Ihrem Controller deklarieren:

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

Dann können Sie in Ihren Aktionen einfach Folgendes schreiben:

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

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

Weitere Informationen zu diesem Ansatz finden Sie in dieser Blogbeitrag und in dieses Codebeispiel.

Aktualisieren:Es wurde auch ein schöner Blogbeitrag geschrieben, der dieses Muster demonstriert Hier.

Verwendung der Aktionskomposition

Tatsächlich ist es oft nützlich, das zu bestehen RequestHeader Wert zu den Vorlagen hinzufügen (siehe z.B. dieses Beispiel).Dies fügt Ihrem Controller-Code nicht so viel Boilerplate hinzu, da Sie problemlos Aktionen schreiben können, die einen impliziten Anforderungswert erhalten:

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

Da Vorlagen häufig mindestens diesen impliziten Parameter erhalten, können Sie ihn durch einen umfangreicheren Wert ersetzen, der z. B.Ihre Menüs.Sie können dies tun, indem Sie die verwenden Zusammensetzung der Aktionen Mechanismus von Spiel 2.

Dazu müssen Sie Ihre definieren Context Klasse, die eine zugrunde liegende Anfrage umschließt:

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

Dann können Sie Folgendes definieren ActionWithMenu Methode:

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

Was wie folgt verwendet werden kann:

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

Und Sie können den Kontext als impliziten Parameter in Ihren Vorlagen verwenden.Z.B.für 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>

Durch die Verwendung der Aktionskomposition können Sie alle impliziten Werte, die Ihre Vorlagen erfordern, in einem einzigen Wert zusammenfassen, aber andererseits verlieren Sie möglicherweise etwas an Flexibilität …

Verwenden von Http.Context (Java)

Da Java nicht über den impliziten Mechanismus von Scala oder ähnliches verfügt, besteht eine Möglichkeit darin, Vorlagenparameter im zu speichern, wenn Sie die explizite Übergabe von Vorlagenparametern vermeiden möchten Http.Context Objekt, das nur für die Dauer einer Anfrage existiert.Dieses Objekt enthält eine args Wert des Typs Map<String, Object>.

Daher können Sie damit beginnen, einen Interceptor zu schreiben, wie in erläutert die Dokumentation:

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

Die statische Methode ist lediglich eine Abkürzung zum Abrufen der Menüs aus dem aktuellen Kontext.Kommentieren Sie dann Ihren Controller, der mit dem gemischt werden soll Menus Aktionsabfangjäger:

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

Rufen Sie abschließend die ab menus Wert aus Ihren Vorlagen wie folgt:

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

Andere Tipps

Ich mache es so, dass ich einfach einen neuen Controller für meine Navigation/mein Menü erstelle und ihn aus der Ansicht aufrufe

So können Sie Ihre definieren 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>
}

Dann kann ich in meiner Hauptansicht das nennen NavController:

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

Ich unterstütze Stians Antwort.Dies ist eine sehr schnelle Möglichkeit, Ergebnisse zu erzielen.

Ich bin gerade von Java+Play1.0 auf Java+Play2.0 migriert und die Vorlagen sind bisher der schwierigste Teil, und die beste Möglichkeit, eine Basisvorlage (für Titel, Kopf usw.) zu implementieren, ist meiner Meinung nach die Verwendung von Http .Kontext.

Es gibt eine sehr schöne Syntax, die Sie mit Tags erreichen können.

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

wo get.scala.html ist:

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

und set.scala.html ist:

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

bedeutet, dass Sie Folgendes in jede beliebige Vorlage schreiben können

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

Es ist also sehr lesbar und schön.

Für diesen Weg habe ich mich entschieden.Stian – guter Rat.Beweist, dass es wichtig ist, nach unten zu scrollen, um alle Antworten zu sehen.:) :)

Übergabe von HTML-Variablen

Ich habe noch nicht herausgefunden, wie man HTML-Variablen übergibt.

@(Titel:String,Inhalt:Html)

Ich weiß jedoch, wie man sie als Block übergibt.

@(title:String)(content:Html)

Daher möchten Sie möglicherweise set.scala.html durch ersetzen

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

Auf diese Weise können Sie HTML-Blöcke wie folgt übergeben

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

BEARBEITEN:Nebeneffekt bei meiner „Set“-Implementierung

Ein häufiger Anwendungsfall ist die Vererbung von Vorlagen in Play.

Sie haben eine base_template.html und dann eine page_template.html, die base_template.html erweitert.

base_template.html könnte etwa so aussehen

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

während die Seitenvorlage in etwa so aussehen könnte

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

und dann haben Sie eine Seite (nehmen wir login_page.html an), die so aussieht

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

@page_template()

Wichtig hierbei ist, dass Sie „body“ zweimal festlegen.Einmal in „login_page.html“ und dann in „page_template.html“.

Es scheint, dass dies einen Nebeneffekt auslöst, solange Sie set.scala.html wie oben vorgeschlagen implementieren.

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

da auf der Seite zweimal „Login Stuff...“ angezeigt wird, da „put“ den Wert zurückgibt, der beim zweiten Mal, wenn wir denselben Schlüssel eingeben, angezeigt wird.(siehe Signatur in Java-Dokumenten einfügen).

Scala bietet eine bessere Möglichkeit, die Karte zu ändern

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

was diese Nebenwirkung nicht verursacht.

Wenn Sie Java verwenden und nur den einfachsten Weg wünschen, ohne einen Interceptor schreiben zu müssen und die @With-Annotation zu verwenden, können Sie auch direkt über die Vorlage auf den HTTP-Kontext zugreifen.

Z.B.Wenn Sie eine Variable benötigen, die aus einer Vorlage verfügbar ist, können Sie sie wie folgt zum HTTP-Kontext hinzufügen:

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

Sie können dann über die Vorlage darauf zugreifen mit:

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

Wenn Sie Ihre Methoden mit Http.Context.current().args.put("","") verunreinigen, ist es natürlich besser, einen Interceptor zu verwenden, aber in einfachen Fällen kann es den Zweck erfüllen.

Aufgrund von Stians Antwort habe ich einen anderen Ansatz versucht.Das funktioniert bei mir.

IM JAVA-CODE

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

IM HTML-VORLAGEKOPF

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

UND VERWENDEN SIE WIE

@if(isOk) {
   <div>OK</div>
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top