在我的特定情况下,我有一个在所有页面上呈现的菜单。菜单内容使用光滑从数据库加载并隐式传递给视图。整件事看起来像这样:
控制器
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)))
}
}
MenuContext
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
等效物?或者甚至可以为(几乎)所有视图中所需的东西提供更好的解决方案?
答案 0 :(得分:5)
制作模板绝对不是一个好主意必须处理Future
- 所以问题就变成了评论中的问题 - 如何非封锁(?)获取来自异步内容源的内容,以及来自其他异步内容源的菜单项?
for
- 对两个Future
个实例的理解就可以解决问题了:
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
块。
不幸的是,虽然它会编译和,但两个任务不会并行运行,从而使练习变得徒劳无功。
答案 1 :(得分:3)
好的,我在这里添加另一个答案,专门尝试DRY up将菜单注入您的操作。
主要问题是您需要在恰当的时间注入菜单,即:
Future
)由于这些限制,我们无法使用ActionBuilder
或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))
}
}
}