Providing feedback to users following some action is important if you don’t want them confused or – worse – annoyed with your application. You can achieve this by adding something to the view following every action, but the question is where? If you add, for example, the information in a div at the top of your page this is noticable but can only contain so much information before you start screwing up your carefully designed page layout.
Another option is to use dialogs – you can get a lot of information in them and they won’t affect your page payout – but they’re a) annoying and b) blocking if modal.
A third option is to use a notification widget, and that’s what this example does. I’ve chosen Eric Hynds’ excellent jquery.notify plugin – it’s small, fast and easily configurable. Take a look at the demos at http://www.erichynds.com/examples/jquery-notify/ to get a feel for them.
Due to the inheritable structure of Play! views, you can define the entire setup of the notification system in your root view and have it automatically handle success or error messages from your flash context. For notifying of AJAX results, a small utility method can handle the dispatch to the correct notify method.
To get started, you’ll need to have jquery and jquery-ui in your application. Add the javascript and css to your root view (e.g. app/views/main.html)
<link rel="stylesheet" type="text/css" href="@{'/public/javascripts/jquery-ui/css/ui-lightness/jquery-ui-1.8.16.custom.css'}" charset="utf-8"> <script src="@{'/public/javascripts/jquery-1.5.2.min.js'}" type="text/javascript" charset="${_response_encoding}"></script> <script src="@{'/public/javascripts/jquery-ui/js/jquery-ui-1.8.16.custom.min.js'}" type="text/javascript" charset="${_response_encoding}"></script>
Next, head on over to https://github.com/ehynds/jquery-notify and download the latest version of jquery.notify. Put the .js and .css files in your favoured locations, and reference them from your root view:
<link rel="stylesheet" type="text/css" href="@{'/public/stylesheets/ui.notify.css'}" charset="utf-8"> <script src="@{'/public/javascripts/jquery.notify.js'}" type="text/javascript" charset="${_response_encoding}"></script>
Next, we need to hook the widget into the view. In the body tag of your root, add the following above the doLayout tag:
<div id="notifyContainer"> <div id="notifyPopup"> <a class="ui-notify-close ui-notify-cross" href="#">x</a> <div style="float:left;margin:0 10px 0 0"><img src="${'#'}{icon}" alt="${'#'}{notificationType}"/></div> <h1>${'#'}{title}</h1> <p>${'#'}{text}</p> </div> </div>
Notice one oddity here – because the templating used by the notify plugin conflicts with Play’s syntax, we have to twist things a bit. Instead of writing, for example, src=”#{icon}” we have to use src=”${‘#’}{icon}”. This will write the literal # into the resulting HTML, and match what notify expects without causing a server-side exception from Play’s rendering engine.
Now it’s time to time to add the javascript support that will do the heavy lifting. This goes into the head of your root view, and consists of three things:
- Globally-available functions that allow the notifications to be invoked from anywhere (including iframes)
- A document-ready block that prepares the notification widget for use
- A hook-up for Play’s flash context
See the in-code comments for an explanation of what’s what. Comments use the Play comment syntax of *{}*
<script type="text/javascript"> *{ Creates the notificaton popup with the given message, icon, etc. All other methods are convenience methods for this. }* function createNotification(notificationContainer, template, vars, opts) { return notificationContainer.notify("create", template, vars, opts); } *{ Convenience method for notifying on AJAX results. See the AjaxResult class in the downloadble example below for the structure of the result parameter }* function ajaxNotify(result) { if (result.success) { notifySuccess(result.message); } else { notifyError(result.message); } } *{ Convenience method for creating success dialogs }* function notifySuccess(message) { createNotification($notifyContainer, "notifyPopup", { title:'Success', text:message, icon:'@{'/public/images/cool-icon.jpg'}', notificationType: 'Success'}, {expires:10000}); } *{ Convenience method for creating error dialogs }* function notifyError(message) { createNotification($notifyContainer, "notifyPopup", { title:'Error', text:message, icon:'@{'/public/images/shame-icon.jpg'}', notificationType: 'Success'}, { expires:false }); } *{ Runs when the document is ready }* $(function() { *{ Prepare the notification widget }* $notifyContainer = $("#notifyContainer").notify(); *{ Create notifications if there is a success or error to report on }* #{if flash?.success} notifySuccess('${flash.success}'); #{/if} #{if flash?.error} notifyError('${flash.error}'); #{/if} }); </script>
Note the images used here can be anything – just put something in your public/images directory and reference it here.
And that’s it! If you have success or error states in your flash, they’ll automatically pop up when the page loads. In the example above, success messages stick around for 10 seconds and then vanish, but error messages have to be manually closed.
As for AJAX calls, you simply need to add a callback function to the call that will invoke the correct method when the call returns. If you call renderJSON() from your controller on an instance of AjaxResult (see the example download), you can use the ajaxNotify() method with the JSON object and it will route to the correct notification automatically.
For example,
$.post('@{Application.ajaxSomethingSuccessful()}', function(result) { ajaxNotify(result); });
You can download the implementation here: notifications. Note that it’s quite a large file because the whole JQuery UI library is included.