scala 如何将 Flash 数据从控制器传递到 Play!框架

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15531455/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 05:04:34  来源:igfitidea点击:

How to pass flash data from controller to view with Play! framework

scalaplayframeworkplayframework-2.0

提问by Ron

I've been plowing through play! so far with a few bumps in the learning curve. Right now I am unable to pass flash data from the controller to the view, which at first I thought was a trivial task, or at least it should be.

我一直在玩游戏!到目前为止,在学习曲线上有一些颠簸。现在我无法将闪存数据从控制器传递到视图,起初我认为这是一项微不足道的任务,或者至少应该如此。

Here's what I have right now:

这是我现在所拥有的:

I have a main layout: application.scala.html

我有一个主要布局:application.scala.html

I have a view that goes in the layout: login.scala.html

我在布局中有一个视图:login.scala.html

and I have my controller and method: UX.authenticate() - I want this to provide flash data to the view depending on the outcome of the login attempt (successful vs fail)

我有我的控制器和方法:UX.authenticate() - 我希望它根据登录尝试的结果(成功与失败)向视图提供闪存数据

This is my code in my controller method:

这是我的控制器方法中的代码:

def authenticate = Action { implicit request =>
        val (email, password) = User.login.bindFromRequest.get
        // Validation
        // -- Make sure nothing is empty
        if(email.isEmpty || password.isEmpty) {
            flash + ("message" -> "Fields cannot be empty") + ("state" -> "error")
            Redirect(routes.UX.login())
        }
        // -- Make sure email address entered is a service email
        val domain = email.split("@")
        if(domain(1) != "example.com" || !"""(\w+)@([\w\.]+)""".r.unapplySeq(email).isDefined) {
            flash + ("message" -> "You are not permitted to access this service") + ("state" -> "error")
            Redirect(routes.UX.login())
        } else {
            // Attempt login
            if(AuthHelper.login(email, password)) {
                // Login successful
                val user = User.findByEmail(email)
                flash + ("message" -> "Login successful") + ("state" -> "success")
                Redirect(routes.UX.manager()).withSession(
                  session + (
                    "user"      -> user.id.toString
                  )
                )
            } else {
                // Bad login
                flash + ("message" -> "Login failed") + ("state" -> "error")
                Redirect(routes.UX.login())
            }
        }
    }

In my login view I have a parameter: @(implicit flash: Flash)

在我的登录视图中,我有一个参数: @(implicit flash: Flash)

When I try to use flash nothing appears using @flash.get("message")

当我尝试使用闪存时,没有出现使用 @flash.get("message")

Ideally I would want to set @(implicit flash: Flash)in the layout, so that I can flash data from any controller and it will reach my view. But whenever I do that, login view throws errors.

理想情况下,我希望@(implicit flash: Flash)在布局中进行设置,以便我可以从任何控制器闪存数据,并且它会到达我的视图。但是每当我这样做时,登录视图都会引发错误。

In my login view right now I have this:

在我的登录视图中,我现在有这个:

def login = Action { implicit request =>
        flash + ("message" -> "test")
        Ok(views.html.ux.login(flash))
    }

What is the ideal way of passing flash data to the view, and are there examples anywhere? The examples on the Play! framework docs do not help whatsoever and are limited to two examples that show no interaction with the view at all (found here at the bottom: http://www.playframework.com/documentation/2.0/ScalaSessionFlash).

将 Flash 数据传递到视图的理想方式是什么,是否有示例?Play 上的例子!框架文档没有任何帮助,仅限于两个完全不显示与视图交互的示例(在底部找到:http: //www.playframework.com/documentation/2.0/ScalaSessionFlash)。

Is there an easier alternative? What am i doing wrong? How can I pass flash data directly to my layout view?

有没有更简单的选择?我究竟做错了什么?如何将 Flash 数据直接传递到我的布局视图?

采纳答案by Ryan

If you look in the documentation for Session and Flash scopesyou'll see this code snippet:

如果您查看Session 和 Flash 范围的文档,您将看到以下代码片段:

def save = Action {
  Redirect("/home").flashing(
    "success" -> "The item has been created"
  )
}

Now, compare that to your use of the flash scope:

现在,将其与您对闪光范围的使用进行比较:

flash + ("message" -> "Login successful") + ("state" -> "success")

The issue with this usage is that flash is immutable, you can't reassign it. Moreover, with your usage here you're actually creating a new flash variable, it just isn't being used.

这种用法的问题是 flash 是不可变的,你不能重新分配它。此外,在这里使用您实际上是在创建一个新的 flash 变量,只是没有使用它。

If you had modified that slightly to become:

如果你稍微修改了一下,变成:

implicit val newFlash = flash + ("message" -> "Login successful") + ("state" -> "success")
Redirect(...)

It would've worked. However, the preferred usage is to use the .flashing()method on your result. This method comes from play.api.mvc.WithHeaders, a trait that is mixed in to play.api.mvc.PlainResult which the various result methods (Ok, Redirect, etc.) inherit from.

它会奏效的。但是,首选用法是对结果使用该.flashing()方法。这个方法来自 play.api.mvc.WithHeaders,这是一个混合到 play.api.mvc.PlainResult 的特性,各种结果方法(Ok、Redirect 等)继承自它。

Then, as shown in the documentation, you can access the flash scope in your template:

然后,如文档中所示,您可以访问模板中的 flash 范围:

@()(implicit flash: Flash) ... 
@flash.get("success").getOrElse("Welcome!") ...

edit: Ah, okay. I've reviewed your sample code and now I see what you're trying to do. I think what you're really looking for is the canonical way of handling form submissions. Review the constraint definitions herein the documentation and I think you'll see there's a better way to accomplish this. Essentially you'll want to use the verifyingmethod on the tuple backing your form so that bindFromRequestwill fail to bind and the validation errors can be passed back to the view:

编辑:啊,好的。我已经查看了您的示例代码,现在我知道您要做什么了。我认为您真正要寻找的是处理表单提交的规范方式。查看约束定义这里的文件中,我想你会看到有一个更好的方式来做到这一点。本质上,您需要verifying在支持表单的元组上使用该方法,以便bindFromRequest绑定失败并且验证错误可以传递回视图:

loginForm.bindFromRequest.fold(
  formWithErrors => // binding failure, you retrieve the form containing errors,
    BadRequest(views.html.login(formWithErrors)),
  value => // binding success, you get the actual value 
    Redirect(routes.HomeController.home).flashing("message" -> "Welcome!" + value.firstName)
)

回答by sdanzig

Wanted to add one more thing to this discussion, to help people avoid this error:

想在此讨论中再添加一件事,以帮助人们避免此错误:

could not find implicit value for parameter flash: play.api.mvc.Flash

I know a lot of this is redundant, but there's a technicality at the end that stole half of my workday and I feel I can help people out with. Appending .flashing(/* your flash scoped info */), such as:

我知道其中很多都是多余的,但最后有一个技术性问题占用了我一半的工作日,我觉得我可以帮助人们解决问题。附加 .flashing(/* 你的 flash 范围信息 */),例如:

Redirect(routes.ImageEditApp.renderFormAction(assetId)).
  flashing("error" -> "New filename must be unused and cannot be empty.")

... doesdefines the implicit "flash" variable that you can use in your template, if you have a "base" template that you want to handle flash scope with (such as errors), singleusage of the base template is easy, since you already have the implicit flash variable defined via .flashing() ... This is an example of one of my "includes" of a base template.

...确实定义了您可以在模板中使用的隐式“flash”变量,如果您有一个“基本”模板要处理 flash 范围(例如错误),则基本模板的单次使用很容易,因为您已经通过 .flashing() 定义了隐式 flash 变量......这是我的基本模板“包含”之一的示例。

@views.html.base("Editing: "+asset.id, scripts = Some(scripts),
  extraNav = Some(nav))

You do nothave to pass the "flash" variable to the base template. It's an implicit. The base template still has to define it. The parameters for my base template is this:

不是要“闪”变量传递给基本模板。这是一个隐含的。基本模板仍然需要定义它。我的基本模板的参数是这样的:

@(title: String, stylesheets: Option[Html] = None,
  scripts: Option[Html] = None,
  extraNav: Option[Html] = None)(content: Html)(implicit flash: Flash)

Yeah, I know a lot of that is unnecessary, but this is a real world example I'm copy n' pasting from. Anyway, it's likely you need to have other templatesthat use your base template, and you do not always use .flashing()to load them. Since you're surely loading these using Controllers, if you forget to start your Action for each with implicit request =>such as:

是的,我知道很多都是不必要的,但这是一个真实世界的例子,我正在复制粘贴。无论如何,您可能需要使用其他模板来使用您的基本模板,并且您并不总是使用 .flashing()来加载它们。由于您肯定使用控制器加载这些,如果您忘记使用隐式请求 =>为每个启动 Action,例如:

def controllerMethodName() = Action { implicit request =>

then the "flash" implicit will not be defined. Then when that template tries to include your base template, you'll be flummoxed because you don't have the default flashimplicit variable defined, while the base template requires it. Hence that error.

那么“flash”隐式将不会被定义。然后,当该模板尝试包含您的基本模板时,您会感到困惑,因为您没有定义默认的 flash隐式变量,而基本模板需要它。因此,该错误。

So again, the fix.. go to all of your controller methods, and make sure you put in that implicit request =>!

再次,修复.. 转到您的所有控制器方法,并确保您放入该隐式请求 =>