play1 では、通常、アクションですべてのデータを取得し、ビューで直接使用します。ビューでパラメータを明示的に宣言する必要がないため、これは非常に簡単です。
しかし、play2 では、ビューの先頭ですべてのパラメーター ( を含むrequest
) を宣言する必要があることがわかりました。アクションですべてのデータを取得してビューに渡すのは非常に面倒です。
たとえば、データベースからロードされたメニューをフロント ページに表示する必要がある場合は、次のように定義する必要があります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>
次に、すべてのサブページでそれを宣言する必要があります。
@(menus: Seq[Menu])
@main("SubPage", menus) {
...
}
次に、メニューを取得して、それをすべてのアクションでビューに渡す必要があります。
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))
}
今のところ、 にはパラメータが 1 つしかありませんがmain.scala.html
、複数ある場合はどうなるでしょうか?
そこで、最終的に、私はすべてをMenu.findAll()
直接視野に入れることにしました。
@(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>
それが良いことなのか、推奨されることなのかはわかりませんが、これに対するより良い解決策はありますか?
ベストアンサー1
私の意見では、テンプレートが静的に型付けされているという事実は、実際には良い要点: テンプレートの呼び出しがコンパイルされた場合、失敗しないことが保証されます。
しかし、確かに呼び出しサイトに定型文が追加されます。しかし減らすことができる(静的型付けの利点を失うことなく)。
Scala では、アクションの構成または暗黙的なパラメータの使用という 2 つの方法でこれを実現できます。Java では、Http.Context.args
マップを使用して有用な値を保存し、テンプレート パラメータとして明示的に渡すことなくテンプレートから取得することをお勧めします。
暗黙のパラメータの使用
menus
パラメータをテンプレート パラメータの最後に配置しmain.scala.html
、「暗黙的」としてマークします。
@(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>
ここで、このメイン テンプレートを呼び出すテンプレートがある場合、これらのテンプレートでも暗黙的なパラメーターとして宣言されていれば、Scala コンパイラによってmenus
パラメーターが暗黙的にテンプレートに渡されるようになります。main
@()(implicit menus: Seq[Menu])
@main("SubPage") {
...
}
ただし、コントローラーから暗黙的に渡されるようにしたい場合は、テンプレートを呼び出すスコープで使用できる暗黙的な値として提供する必要があります。たとえば、コントローラーで次のメソッドを宣言できます。
implicit val menu: Seq[Menu] = Menu.findAll
次に、アクションに次のように記述します。
def index = Action {
Ok(views.html.index())
}
def index2 = Action {
Ok(views.html.index2())
}
このアプローチの詳細については、このブログ投稿そしてこのコードサンプル。
アップデート: このパターンを示す素晴らしいブログ記事も書かれていますここ。
アクションコンポジションの使用
RequestHeader
実際には、テンプレートに値を渡すと便利なことがよくあります(例を参照)。このサンプル)。暗黙的なリクエスト値を受け取るアクションを簡単に記述できるため、コントローラー コードにそれほど多くの定型文が追加されることはありません。
def index = Action { implicit request =>
Ok(views.html.index()) // The `request` value is implicitly passed by the compiler
}
テンプレートは少なくともこの暗黙のパラメータを受け取ることが多いので、メニューなどを含むより豊富な値に置き換えることができます。アクション構成プレイ2の仕組み。
Context
そのためには、基礎となるリクエストをラップするクラスを定義する必要があります。
case class Context(menus: Seq[Menu], request: Request[AnyContent])
extends WrappedRequest(request)
次に、次のActionWithMenu
メソッドを定義できます。
def ActionWithMenu(f: Context => Result) = {
Action { request =>
f(Context(Menu.findAll, request))
}
}
これは次のように使用できます。
def index = ActionWithMenu { implicit context =>
Ok(views.html.index())
}
また、テンプレート内でコンテキストを暗黙的なパラメータとして受け取ることもできます。例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>
アクション構成を使用すると、テンプレートに必要なすべての暗黙的な値を 1 つの値に集約できますが、その一方で柔軟性が失われる可能性があります...
Http.Context の使用 (Java)
Java には Scala の暗黙のメカニズムなどがないため、テンプレート パラメータを明示的に渡すことを避けたい場合は、リクエストの期間中のみ存続するオブジェクトにパラメータを格納するという方法があります。このオブジェクトには、 型の値がHttp.Context
含まれます。args
Map<String, Object>
したがって、インターセプターを記述することから始めることができます。ドキュメント:
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");
}
}
静的メソッドは、現在のコンテキストからメニューを取得するための単なるショートカットです。次に、Menus
アクション インターセプターと混合されるようにコントローラーに注釈を付けます。
@With(Menus.class)
public class Application extends Controller {
// …
}
最後に、menus
次のようにしてテンプレートから値を取得します。
@(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>