如何避免在播放2中到处传递参数?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (16)

在播放1中,我通常在操作中获取所有数据,并在视图中直接使用它们。因为我们不需要显式地声明视图中的参数,所以这非常容易。

但是在第2场比赛中,我发现我们必须声明所有的参数(包括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))
}

到目前为止,它只是一个参数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>

我不知道它是好的还是推荐的,有什么更好的解决方案吗?

提问于
用户回答回答于

通过动作组合或使用隐式参数。在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") {
  ...
}

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

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
}

因此,由于模板通常至少接收到这个隐式参数,所以可以用包含菜单等内容的更丰富的值来替换它。

要做到这一点,必须定义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>

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

使用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>
用户回答回答于

你可以定义你的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>
}

在我的主要观点中,我可以称之为NavController:

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

扫码关注云+社区