playframework-2.0 - 如何避免在play2中到处传递参数?

标签 playframework-2.0

在play1中,我通常在actions中获取所有数据,直接在views中使用它们。由于我们不需要在 View 中显式声明参数,因此这非常简单。

但是在play2中,我发现我们必须在 View 的头部声明所有参数(包括request),在actions中获取所有数据并将它们传递到 View 中会非常无聊。

例如,如果我需要在首页显示从数据库加载的菜单,我必须在 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))
}

目前 main.scala.html 中只有一个参数,如果有多个参数怎么办?

所以最后,我决定将所有 Menu.findAll() 直接放在 View 中:

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

不知道好不好,是否值得推荐,有没有更好的解决方案?

最佳答案

在我看来,模板是静态类型的这一事实实际上是一件好事:保证您在编译时调用模板不会失败。

但是,它确实在调用站点上添加了一些样板。但是您可以减少它(不会失去静态类型优势)。

在 Scala 中,我看到有两种方法可以实现它:通过操作组合或使用隐式参数。在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>

现在,如果您有模板调用此主模板,则可以拥有 menus参数隐式传递给 main如果 Scala 编译器也将模板声明为这些模板中的隐式参数:

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

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

但是,如果您希望从 Controller 隐式传递它,则需要将其作为隐式值提供,该值在调用模板的范围内可用。例如,您可以在 Controller 中声明以下方法:

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

然后在您的操作中,您将能够编写以下内容:

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

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

您可以在this blog post中找到有关此方法的更多信息。并在 this code sample .

更新:还写了一篇很好的博客文章来演示这种模式 here .

使用 Action 组合

实际上,传递 RequestHeader 通常很有用。模板的值(参见例如 this sample )。这不会向您的 Controller 代码添加太多样板,因为您可以轻松编写接收隐式请求值的操作:

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

因此,由于模板通常至少会收到此隐式参数,因此您可以将其替换为包含例如的更丰富的值。你的菜单。您可以使用 actions composition 来做到这一点Play 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>

使用操作组合允许您将模板所需的所有隐式值聚合为单个值,但另一方面您可能会失去一些灵 active ......

使用 Http.Context (Java)

由于 Java 没有 Scala 的隐式机制或类似机制,如果您想避免显式传递模板参数,一种可能的方法是将它们存储在 Http.Context 中。仅在请求期间存在的对象。该对象包含 args类型值Map<String, Object> .

因此,您可以从编写拦截器开始,如 the documentation 中所述。 :

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

静态方法只是从当前上下文中检索菜单的简写。 然后将您的 Controller 注释为与 Menus 混合。 Action 拦截器:

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

关于playframework-2.0 - 如何避免在play2中到处传递参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9629250/

相关文章:

postgresql - Scala Play "Can' t 解析符号 db"

java - Play2 - 表单 -> objectId

playframework - 用于 Play Framework 的 Firefox Selenium webdriver : How to upgrade?

scala - 在scala中实现长轮询并用akka玩2.0

playframework-2.0 - SnakeYaml 通过静态方法创建对象

forms - 重复值的唯一约束

java - 如何创建基于 Java 的 Play 框架应用程序(非 Scala)

playframework-2.0 - 将模板中的可重用 block 包含到另一个模板中

java - Play Scala 从另一个方法调用 Controller 方法

java - 如何在java的 Play Framework 中使用属性文件?