如何处理播放视图中隐式传递的期货
How to handle implicitly passed futures in play views
在我的特殊情况下,我有一个在所有页面上呈现的菜单。菜单内容使用 slick 从数据库加载并隐式传递给视图。整个事情看起来像这样:
控制器
class Application @Inject()(
implicit val menuContext: MenuContext
) extends Controller {
def index = Action.async {
val content: Future[Content] = getContent
content.map(c => Ok(views.html.index(c)))
}
}
菜单上下文
class MenuContext {
val models: Future[List[SomeModel]] = getModelsFromDB
}
查看
@(content: Content)(implicit menuContext: MenuContext)
...
@menuContext.models // how to access my actual model and not the Future?
...
如何在我的视图中访问 List[SomeModel]
?是否有一个 Action.async
等价物用于传递隐式参数?或者对于(几乎)所有视图中所需的内容,是否有更好的解决方案?
使模板必须处理 Future
绝对不是一个好主意 - 所以问题变成了你评论中的问题 - 如何 非阻塞 (?) 从您的异步内容源获取内容,以及从您的其他异步内容源获取您的菜单项?
对两个 Future
实例的 for
理解就可以了:
def index = Action.async {
val fContent:Future[Content] = getContent
val fMenus:Future[List[SomeModel] = getModelsFromDB
for {
content <- fContent
menus <- fMenus
} yield(Ok(views.html.index(content)(menus))))
}
注意:您可能想尝试节省几行并将方法调用(getContent
、getModelsFromDB
)直接放入for
块。
不幸的是,虽然它会编译 和 工作,但这两个任务 不会 运行 并行,从而使练习有些徒劳.
好的,我在这里添加另一个答案,专门尝试 DRY up 将菜单注入到您的操作中。
主要问题是您需要在恰到好处的时间注入菜单,即:
- 当您准备好数据时(或至少
Future
持有数据)
- 当您知道要呈现哪个模板时
- 当您知道您将返回什么状态代码时
由于这些限制,我们不能使用 ActionBuilder
or ActionRefiner
- 他们假设您的内部控制器代码块将生成完成的 Result
.
因此,我们将定义一个可以混合到控制器中的特征:
trait MenuDecoration {
def withMenuSimple(body: Future[List[SomeModel] => Result]):Future[Result] = {
val fm = getModelsFromDB
val fb = body
for {
m <- fm
b <- fb
} yield(b(m))
}
}
从我的另一个回答来看,这应该看起来很熟悉,并且它的工作方式相同 - 它将开始执行两个异步任务,并在它们完成后将它们放在一起。
需要用菜单装饰模板的Action
如下所示:
class BlahController extends Controller with MenuDecoration {
def index = Action.async {
withMenuSimple {
getContent.map { content => implicit menu =>
Ok(views.html.index(content))
}
}
}
}
为什么 withMenuSimple
?因为在某些时候你可能想要检查 Request
- 所以我们有这个选择:
trait MenuDecoration {
...
def withMenu(body: RequestHeader => Future[List[SomeModel] => Result])(implicit request:RequestHeader):Future[Result] = {
val fm = fMenus
val fb = body(request)
for {
m <- fm
b <- fb
} yield(b(m))
}
}
您可以这样使用:
def indexWithReq = Action.async { implicit request =>
withMenu { req =>
getContent.map { content => implicit menu =>
Ok(views.html.index(content))
}
}
}
在我的特殊情况下,我有一个在所有页面上呈现的菜单。菜单内容使用 slick 从数据库加载并隐式传递给视图。整个事情看起来像这样:
控制器
class Application @Inject()(
implicit val menuContext: MenuContext
) extends Controller {
def index = Action.async {
val content: Future[Content] = getContent
content.map(c => Ok(views.html.index(c)))
}
}
菜单上下文
class MenuContext {
val models: Future[List[SomeModel]] = getModelsFromDB
}
查看
@(content: Content)(implicit menuContext: MenuContext)
...
@menuContext.models // how to access my actual model and not the Future?
...
如何在我的视图中访问 List[SomeModel]
?是否有一个 Action.async
等价物用于传递隐式参数?或者对于(几乎)所有视图中所需的内容,是否有更好的解决方案?
使模板必须处理 Future
绝对不是一个好主意 - 所以问题变成了你评论中的问题 - 如何 非阻塞 (?) 从您的异步内容源获取内容,以及从您的其他异步内容源获取您的菜单项?
对两个 Future
实例的 for
理解就可以了:
def index = Action.async {
val fContent:Future[Content] = getContent
val fMenus:Future[List[SomeModel] = getModelsFromDB
for {
content <- fContent
menus <- fMenus
} yield(Ok(views.html.index(content)(menus))))
}
注意:您可能想尝试节省几行并将方法调用(getContent
、getModelsFromDB
)直接放入for
块。
不幸的是,虽然它会编译 和 工作,但这两个任务 不会 运行 并行,从而使练习有些徒劳.
好的,我在这里添加另一个答案,专门尝试 DRY up 将菜单注入到您的操作中。
主要问题是您需要在恰到好处的时间注入菜单,即:
- 当您准备好数据时(或至少
Future
持有数据) - 当您知道要呈现哪个模板时
- 当您知道您将返回什么状态代码时
由于这些限制,我们不能使用 ActionBuilder
or ActionRefiner
- 他们假设您的内部控制器代码块将生成完成的 Result
.
因此,我们将定义一个可以混合到控制器中的特征:
trait MenuDecoration {
def withMenuSimple(body: Future[List[SomeModel] => Result]):Future[Result] = {
val fm = getModelsFromDB
val fb = body
for {
m <- fm
b <- fb
} yield(b(m))
}
}
从我的另一个回答来看,这应该看起来很熟悉,并且它的工作方式相同 - 它将开始执行两个异步任务,并在它们完成后将它们放在一起。
需要用菜单装饰模板的Action
如下所示:
class BlahController extends Controller with MenuDecoration {
def index = Action.async {
withMenuSimple {
getContent.map { content => implicit menu =>
Ok(views.html.index(content))
}
}
}
}
为什么 withMenuSimple
?因为在某些时候你可能想要检查 Request
- 所以我们有这个选择:
trait MenuDecoration {
...
def withMenu(body: RequestHeader => Future[List[SomeModel] => Result])(implicit request:RequestHeader):Future[Result] = {
val fm = fMenus
val fb = body(request)
for {
m <- fm
b <- fb
} yield(b(m))
}
}
您可以这样使用:
def indexWithReq = Action.async { implicit request =>
withMenu { req =>
getContent.map { content => implicit menu =>
Ok(views.html.index(content))
}
}
}