Using the Flash
Using the Flash to pass data from one request to the next.
The Flash is actually a very simple concept. And no, it has nothing to do with
Adobe's Flash Player.
The Flash is just a struct in the
session
or cookie
scope with some added
functionality. It is cleared at the end of the next page that the user views.
This means that it's a good fit for storing messages or variables temporarily
from one request to the next.By the way, the name "Flash" comes from Ruby on Rails, like so many other cool
things in Wheels.
The code below is commonly used in Wheels applications to store a message about
an error in the Flash and then redirect to another URL, which then displays the message in its view page.
The following example shows how code dealing with the Flash can look in an
action that handles a form submission.
function update() {
if ( user.update(params.user) ) {
flashInsert(success="The user was updated successfully.");
redirectTo(action="edit");
} else {
renderView(action="edit");
}
}
Here's an example of how we then display the Flash message we just set in the
view page for the
edit
action. Please note that this is done on the next
request since we performed a redirect after setting the Flash.<cfoutput>
<p class="success-message">
#flash("success")#
</p>
</cfoutput>
As you can see above, you use the flashInsert() function with a named argument when you want to store data in the Flash and the flash() function when you want to display the data in a view.
The key chosen above is
success
, but it could have been anything that we wanted. Just like with a normal struct, the naming of the keys is your job.As an example, you may choose to use one key for storing messages after the user made an error, called
error
, and another one for storing messages after a successful user operation, called success
.The more you work with Wheels and the Flash, the more that you're going to find
that you keep repeating that flashInsert()/ redirectTo() combo all
the time. Wheels has a solution for that within the redirectTo() function
itself:
redirectTo(action="edit", success="The user was updated successfully.");
That piece of code does exactly the same thing as the example shown previously
in this chapter. The Wheels redirectTo() function sees the
success
argument coming in and knows that it's not part of its own declared arguments.Therefore, you (the developer) must intend for it to be stored in the Flash,
so Wheels goes ahead and calls
flashInsert(success="The user was updated successfully.")
for you behind the scenes.So what if you want to redirect to the
edit
action and set a key in the Flash
named action
as well? Simply prepend the key with flash
to tell Wheels to
avoid the argument naming collision.CFScript
redirectTo(action="edit", flashAction="The user was updated successfully.");
We don't recommend naming the keys in your Flash
action
, but these naming
collisions can potentially happen when you want to redirect to a route that
takes custom arguments, so remember this workaround.Besides flash() and flashInsert() that are used to read
from/insert to the Flash, there are a few other functions worth mentioning.
flashClear() and flashDelete() do exactly the same as their
counterparts in the struct world,
StructClear
and StructDelete
—they clear
the entire Flash and delete a specific key/value from it, respectively.flashKeyExists() is used to check if a specific key exists. So it would
make sense to make use of that function in the code listed above to avoid
outputting an empty
<p>
tag on requests where the Flash is empty.
( flash() will return an empty string when the specified key does not
exist.)Check out the Controller > Flash Functions section in the API listing of all the functions that deal with the Flash.
Throw the flashIsEmpty() function into the mix, and you might find
yourself writing code across your Wheels projects that looks something like
this:
<cfoutput>
<cfif NOT flashIsEmpty()>
<div id="flash-messages">
<cfif flashKeyExists("error")>
<p class="errorMessage">
#flash("error")#
</p>
</cfif>
<cfif flashKeyExists("success")>
<p class="successMessage">
#flash("success")#
</p>
</cfif>
</div>
</cfif>
</cfoutput>
<cfoutput>
#flashMessages()#
</cfoutput>
Whenever any value is inserted into the Flash, flashMessages() will
display it similarly to the complex example above, with
class
attributes set
similarly (errorMessage
for the error
key and successMessage
for the
success
key).You can also use flashMessages()'s
key/keys
argument to limit its reach
to a list of given keys. Let's say that we only want our layout to show messages
for the alert
key but not for the error
or success
keys (or any other for
that matter). We would write our call like so:<cfoutput>
#flashMessages(key="alert")#
</cfoutput>
Just keep in mind that this approach isn't as flexible, so if you need to
customize the markup of the messages beyond flashMessages()'s
capabilities, you should revert back to using flashIsEmpty(),
flash(), and other related functions manually.
Earlier, we mentioned that the data for the Flash is stored in either the
cookie
or the session
scope. You can find out where Wheels stores the Flash
data in your application by outputting get("flashStorage")
. If you have
session management enabled in your application, Wheels will default to storing
the Flash in the session
scope. Otherwise, it will store it in a cookie on the
user's computer.You can override this setting in the same way that you override other Wheels
settings by running the set() function like this:
//In `config/settings.cfm` or another `settings.cfm` file within the `config` subfolders
set(flashStorage="session");
Note: Before you set Wheels to use the
session
scope, you need to make sure
that session management is enabled. To enable it, all you need to do is add
this.SessionManagement = true
to the config/app.cfm
file.So what storage option should you choose? Well, to be honest, it doesn't really
matter that much, so we recommend you just go with the default setting. If
you're a control freak and always want to use the optimal setting, here are some
considerations.
- Although the Flash data is deserialized before stored in a cookie (making it possible to store complex values), you need to remember that a cookie is not the best place to store data that requires a lot of space.
- If you run multiple ColdFusion servers in a clustered environment and use session-based Flash storage, users might experience a loss of their Flash variables as their request gets passed to other servers.
- Using cookies is, generally speaking, less secure than using the
session
scope. Users could open their cookie file up and manually change its value. Sessions are stored on the server, out of users' reach.
From CFWheels 2.1, you can now change the default flash behavior to append to an existing key, rather than directly replacing it. To turn on this behavior, add
set(flashAppend=true)
to you /config/settings.cfm
file.An example of where this might be useful:
// In your controller
if( thingOneHappened ){
flashInsert(info="Thing One Happened");
}
if( thingTwoHappened ){
flashInsert(info="Thing Two Happened");
}
Which, when output via
flashMessages()
would render:<div class=""flash-messages"">
<p class=""info-message"">Thing One Happened</p>
<p class=""info-message"">Thing Two Happened</p>
</div>
With
set(flashAppend=true)
, you can also directly pass in an array of strings like so:msgs=[
"Thing One Happened", "Thing Two Happened"
];
flashInsert(info=msgs);
In
2.2
upwards:There are certain circumstances where you might not be using the flash: for example, if you have wheels setup as a stateless API (perhaps in a cluster behind a load balancer) where you're using tokens vs sessions. In this circumstance, you've probably turned off session management, which only leaves
cookie
as a viable route. But if set to cookie
, you always get Set-Cookie
being returned, which is unnecessary.In wheels 2.2 you can now
set(flashStorage = "none")
which will prevent the creation of the cookie, and also not try and write to a session.