如何在播放视图中处理隐式传递的期货

时间:2015-05-28 10:15:16

标签: scala playframework playframework-2.0 slick playframework-2.4

在我的特定情况下,我有一个在所有页面上呈现的菜单。菜单内容使用光滑从数据库加载并隐式传递给视图。整件事看起来像这样:

控制器

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等效物?或者甚至可以为(几乎)所有视图中所需的东西提供更好的解决方案?

2 个答案:

答案 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))))
} 

注意:您可能想尝试保存几行并将方法调用(getContentgetModelsFromDB)直接放入for块。 不幸的是,虽然它会编译,但两个任务不会并行运行,从而使练习变得徒劳无功。

答案 1 :(得分:3)

好的,我在这里添加另一个答案,专门尝试DRY up将菜单注入您的操作。

主要问题是您需要在恰当的时间注入菜单,即:

  • 准备好数据(或至少保留数据Future
  • 当您知道要呈现哪个模板时
  • 当您知道要返回的状态代码时

由于这些限制,我们无法使用ActionBuilderActionRefiner - 他们假设您的内部控制器代码块将生成完成的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))
    }
  }
}