Flashing
Most tutorials in this wiki use flash() to provide feedback to the user. I thought I would show my preferred way to flash.
The common way to flash looks like this as part of an edit action in a controller:
if($this->Dummy->save()) { $this->flash('Saved changes successfully','/'); }
The result is a generic page showing the message as a link. Clicking the message takes you in this case to the index page. From a usability standpoint I much prefer the user to be redirected to the destination page directly and the flash message to be included as part of the layout of that page. To accomplish this I take advantage of the Session component in CakePHP to store the flash message for later retrieval.
The same dummy code would then look like this:
if($this->Dummy->save()) { $this->Session->setFlash('Saved changes successfully'); $this->redirect('/'); }
To display this message you need to add a little something to your view files or lauout file. I ususally keep it in my main application layout. The code is quite simple and looks like this:
<html>
<head>
<title> <?php echo $title_for_layout; ?> </title>
</head>
<body>
<?php $this->controller->Session->flash(); ?>
<?php echo $content_for_layout; ?>
</body>
</html>
This will insert a simple div-tag into your layout containing the flash message.
<div class="message">Saved changes successfully</div>
Improving the Message display
This is not very attractive. I want this message to be clearly visible as an application message and further more I don’t want this message to show for more than a few seconds. To get something that works a little better we’lll add some CSS and some Javascript.
First, some CSS to style the message div. This code makes the div an informative yellow color with a border. Then it sticks it at the top of the browser window (just right of my logo).
.message { position:absolute; top:0px; left:100px; width:300px; font-size:14px; border: solid 1px #c9b484; border-top:0px; padding:12px; color:#000; background-color:#fff9d9; text-align:center; }
Now we turn to the javascript. Since the message div is not given an ID by Cake I created this loop to find it by its class and hide it. It is activated by a delayed onload handler on the body of the layout document.
There is a simpler way of locating the message than the one below. If you are using the prototype library you can make use of document.getElementsByClassName to simplify things a bit.
var allPageTags = new Array(); function hideFlash() { var theClass = 'message'; var allPageTags=document.getElementsByTagName("div"); for (i=0; i<allPageTags.length; i++) { if (allPageTags[i].className==theClass) { allPageTags[i].style.display='none'; } } }
Putting this all together in a layout could look like this:
<html>
<head>
<title> <?php echo $title_for_layout; ?> </title>
<style type="text/css">
.message
{
position:absolute;
top:0px;
left:100px;
width:300px;
font-size:14px;
border: solid 1px #c9b484;
border-top:0px;
padding:12px;
color:#000;
background-color:#fff9d9;
text-align:center;
}
</style>
<script language="javascript" type="text/javascript">
<!--
var allPageTags = new Array();
function hideFlash()
{
var theClass = 'message';
var allPageTags=document.getElementsByTagName("div");
for (i=0; i<allPageTags.length; i++)
{
if (allPageTags[i].className==theClass)
{
allPageTags[i].style.display='none';
}
}
}
//-->
</script>
</head>
<body onload="setTimeout('hideFlash()',3000);" >
<?php $this->controller->Session->flash(); ?>
<?php echo $content_for_layout; ?>
</body>
</html>
My application now shows a nicely formatted information message after performing a successful save and in a few other occasions.
A simpler way of hiding
1)You maybe able to hide the box without using javascript in certain layouts.
All you need is to set the .message class to display:inline, which will make the message box collapse completely if it doesn’t have content. For example:
.message { display:inline; padding:3px 8px; font-weight:bold; color:#000000; background-color:#FFFEEA; border-bottom:2px solid #FFFCB9; }
alternate CSS flash note
2)While digging around in the code, and then asking PhpNut_ on #cakephp, I found that all this workaround with javascript can be avoided.
If you want your flash message to have an id attribute, the answer is to create a layout. If you look in the API http://api.cakephp.org/class_session_component.html#a3f170a518df33b727d1e5bebb7801bb , you will see that Session::flash() takes 4 parameters. $flashMessage is what we always use, but the second parameter is $layout.
All you have to do to avoid all the javascript to find the message css is this. create a layout as simple as follows
<div id='sessionFlash' class='message'><?php echo $content_for_layout ?></div>
Let’s say we named that sessionFlash.thtml and put it in our layouts directory. Now instead of this:
$this->Session->setFlash('Saved changes successfully');
We would use
$this->Session->setFlash('Saved changes successfully','sessionFlash');
Our flash message will now have the id that we set. This saves us from having to add the javascript to find the element, it also makes hiding the element a bit more concise as well. We can still use the regular class style to set the look of the element, so that it can be shared among other elements if you desire, but it also gives us complete and simple DOM access to the element.
By the way, in our layout, the $content_for_layout gets replaced with our flash message, not the main body content. Don’t get confused just because they have the same name. Session:setFlash() calls View::renderLayout() to render the layout for the flash message, and passes your $flashMessage as $content_for_layout. It’s all in the code, waiting for you to find it.
Using all the bells and whistles
Pretty soon I discover that I want even more. I want to have a few different types of messages for different occations and I want them to be styled a little differently.
- A basic info message in yellow with an “i” icon preceding the message text.
- An error message in red with a warning icon.
- An OK message in green also with an icon of its own.
First we revisit the controller code and add another parameter to setFlash.
if($this->Dummy->save()) { $this->Session->setFlash('Saved changes successfully','message_ok'); $this->redirect('/'); }
This tells setFlash to look for a layout file called message_ok.thtml and insert the message into it. This layout file should be located in /app/views/layout and will looks like this:
<div class="message" style="background-color:#cfc;border-color:#6d6;">Success!
<?php echo $content_for_layout ?>
</div>
I have left it very simple for this tutorial. All changes in style have been added inline but should in “real life” be CSS classes. The flash message will replace $content_for_layout as in any other layout file. Now I just have to create a few more and choose the right ones in my actions. For successful saves and updates I will use my green OK layout shown above. For failures I will display the message in my red error layout and so on.
E.g. you could now add a nifty effect to make your messages appear in strong colors and then fade out to lighter ones. The Fade Anything Technique by Adam Michela (http://www.axentric.com/aside/fat/) is perfect for that purpose! First include the js-file in the header of your layout:
[...]
<?php echo $javascript->link('fat.js'); ?>
[...]
Then the only thing you need to do is add another className:
<div class="message fade-30FF30" style="background-color:#cfc;border-color:#6d6;">Success!
<?php echo $content_for_layout ?>
</div>
And you’re done!
There is one more section to be added here so check back later.
Thanks to Nate for proof-reading and suggesting improvements.