2

在我的特殊情况下,我有一个在所有页面上呈现的菜单。菜单内容使用 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传递隐式参数是否有等价物?或者对于(几乎)所有视图中需要的东西,是否有更好的解决方案?

4

2 回答 2

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块中。不幸的是,虽然它可以编译工作,但这两个任务不会并行运行,从而使练习有些徒劳。

于 2015-05-28T11:26:14.253 回答
3

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

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

  • 当您准备好数据时(或至少Future持有数据)
  • 当您知道要渲染哪个模板时
  • 当您知道要返回的状态码时

由于这些限制,我们不能使用ActionBuilderor 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))
    }
  }
}
于 2015-05-29T05:17:52.877 回答