如何避免在play2中到处传递参数? [英] How to avoid passing parameters everywhere in play2?

查看:77
本文介绍了如何避免在play2中到处传递参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在play1中,我通常会获得动作中的所有数据,并直接在视图中使用它们.由于我们不需要在视图中显式声明参数,因此非常简单.

In play1, I usually get all data in actions, use them directly in views. Since we don't need to explicitly declare parameters in view, this is very easy.

但是在play2中,我发现我们必须在视图的头部声明所有参数(包括request),要获取所有数据并将其传递到视图中将非常无聊.

But in play2, I found we have to declare all the parameters(including request) in the head of views, it will be very boring to get all data in actions and pass them into views.

例如,如果需要在首页显示从数据库加载的菜单,则必须在main.scala.html中定义它:

For example, if I need to display menus which are loaded from database in the front page, I have to define it in 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>

然后我必须在每个子页面中声明它:

Then I have to declare it in every sub page:

@(menus: Seq[Menu])

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

然后,我必须获取菜单并将其传递给每个动作以进行查看:

Then I have to get the menus and pass it to view in every action:

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中的一个参数,如果有很多,该怎么办?

For now it's only one parameter in main.scala.html, what if there are many?

所以最后,我决定直接查看所有Menu.findAll():

So at last, I decided to all Menu.findAll() directly in 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>

我不知道这是好还是推荐,对此是否有更好的解决方案?

I don't know if it is good or recommended, is there any better solution for this?

推荐答案

在我看来,模板是静态类型的事实实际上是的事情:您可以保证调用模板会如果编译就不会失败.

In my opinion, the fact that templates are statically typed is actually a good thing: you’re guaranteed that calling your template will not fail if it compiles.

但是,它确实在调用站点上增加了一些样板.但是您可以减少它(而不会失去静态键入优势).

However, it indeed adds some boilerplate on the calling sites. But you can reduce it (without losing static typing advantages).

在Scala中,我看到两种方法来实现它:通过动作组合或使用隐式参数.在Java中,我建议使用Http.Context.args映射存储有用的值,并从模板中检索有用的值,而不必显式传递为模板参数.

In Scala, I see two ways to achieve it: through actions composition or by using implicit parameters. In Java I suggest using the Http.Context.args map to store useful values and retrieve them from the templates without having to explicitly pass as templates parameters.

menus参数放在main.scala.html模板参数的末尾,并将其标记为隐式":

Place the menus parameter at the end of your main.scala.html template parameters and mark it as "implicit":

@(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模板,前提是这些参数也被声明为这些模板中的隐式参数:

Now if you have templates calling this main template, you can have the menus parameter implicitly passed for you to the main template by the Scala compiler if it is declared as an implicit parameter in these templates as well:

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

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

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

But if you want to have it implicitly passed from your controller you need to provide it as an implicit value, available in the scope from where you call the template. For instance, you can declare the following method in your controller:

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

然后,您可以在操作中编写以下内容:

Then in your actions you’ll be able to just write the following:

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

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

您可以在此代码示例.

更新:此处.

实际上,将RequestHeader值传递给模板通常很有用(请参见例如

Actually, it’s often useful to pass the RequestHeader value to the templates (see e.g. this sample). This does not add so much boilerplate to your controller code because you can easily write actions receiving an implicit request value:

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

因此,由于模板通常至少会收到此隐式参数,因此您可以将其替换为包含例如的更丰富的值.您的菜单.您可以通过使用Play 2的动作组成机制来做到这一点.

So, since templates often receive at least this implicit parameter, you could replace it with a richer value containing e.g. your menus. You can do that by using the actions composition mechanism of Play 2.

为此,您必须定义Context类,并包装基础请求:

To do that you have to define your Context class, wrapping an underlying request:

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

然后,您可以定义以下ActionWithMenu方法:

Then you can define the following ActionWithMenu method:

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

可以这样使用:

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

您可以将上下文作为模板中的隐式参数.例如.对于main.scala.html:

And you can take the context as an implicit parameter in your templates. E.g. for 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>

使用动作组合使您可以将模板所需的所有隐式值聚合为一个值,但是另一方面,您可能会失去一些灵活性...

Using actions composition allows you to aggregate all the implicit values your templates require into a single value, but on the other hand you can lose some flexibility…

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

Since Java does not have Scala’s implicits mechanism or similar, if you want to avoid to explicitly pass templates parameters a possible way is to store them in the Http.Context object which lives only for the duration of a request. This object contains an args value of type Map<String, Object>.

因此,您可以先编写拦截器,如文档中所述:

Thus, you can start by writing an interceptor, as explained in 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");
    }
}

static方法只是从当前上下文中检索菜单的简写. 然后注释您的控制器以与Menus动作拦截器混合:

The static method is just a shorthand to retrieve the menus from the current context. Then annotate your controller to be mixed with the Menus action interceptor:

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

最后,如下所示从模板中检索menus值:

Finally, retrieve the menus value from your templates as follows:

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

这篇关于如何避免在play2中到处传递参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆