score:8

Accepted answer

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

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.

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.

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

@()(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 here in the documentation and I think you'll see there's a better way to accomplish this. Essentially you'll want to use the verifying method on the tuple backing your form so that bindFromRequest will fail to bind and the validation errors can be passed back to the view:

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)
)

score:5

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:

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

... does defines 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), single usage 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.

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

You do not have 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 templates that 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:

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 flash implicit variable defined, while the base template requires it. Hence that error.

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


Related Query

More Query from same tag