A question was asked in this thread on the APEX forums that got me itching to do a science experiment.
The basic question was this:

"Is it possible to produce 'Growl' style notifications in place of the standard APEX Success and Failure messages?"
In short the answer is an unequivocal 'Yes', and here's how:

First, unless you're a JavaScript whiz and can write your own, you'll need to search out some sort of JavaScript library that provides the Growl style notification. After a quick Google search I found this page...

http://webtoolkit4.me/2009/08/13/jquery-growl-likenotification-systems/

I chose to search for jQuery related notification systems because of the fact that jQuery is already integrated into APEX 4 and that would mean that there would be less in terms of external JavaScripts for me to load and manage.

After looking at the individual option there, I chose to work with Gritter for a number of reasons.
  1. I liked the look and feel of it's output. To me it felt the most like what I am used to seeing from Growl.
  2. The JavaScript is very light. In total it's about 400 lines long un-minified. Much of that is white space and comments which means that minified it's going to be minute.
  3. It pushes most of the heavy lifting back onto jQuery, where it should be, and the author doesn't try to over engineer the solution.
You can choose what ever library you wish, but in this example I'll be using Gritter.

The first thing you need to do is put the Gritter JavaScript where it can be accessed by APEX. As a general rule of thumb, it's a very bad habit to get into to put your own code into the /i/ directory provided by Oracle. So we create a directory on our web server to hold all of our custom code, normally named /c/. Inside this directory I created a directory to hold the gritter objects. I ended up with the following directory entries:

/c/gritter/js
/c/gritter/css
/c/gritter/images

The next thing to do is to include these scripts into your application. I wanted to see if I could completely replace the standard way APEX handles SUCCESS and FAILURE messages, so I want these scripts to be available on every page. There are two ways I could do this: I could create an After Header region on Page Zero that loads the scripts, or I could edit the page template and insert the script calls there.

To make things as quick and easy as possible to load and troubleshoot, I chose to create a Page Zero region as follows:

Region Name: GRITTER - INCLUDES
Region Type: HTML Text
Template Type: No Template
Display Point: After Header

and include the following code:

<link href="/c/gritter/css/jquery.gritter.css" type="text/css" rel="stylesheet" />
<script src="/c/gritter/js/jquery.gritter.min.js" type="text/javascript"></script>
 
After loading the scripts and doing a few tests, I found out a few things...

  1. You can use JavaScript in the Success and Failure message of any validation or process and it will be executed. News to me and something I'll file away for later use!
  2. This isn't scalable as you'd have to change every message within your application to a JavaScript call.
  3. Because of the way JavaScript deals with nested quotes within strings, life gets extremely interesting when the message you're trying to relay to the end user contains either single or double quotes.
To make this work at the application level without forcing the developer to use JavaScript for every message we need to go a step higher and look at the templates. If you're unfamiliar with APEX templates you may not know that within a page template there are two sub-templates for the Success and Notification (failure) messages. By editing these areas within your page template you can move the handling of the JavaScript up so that the developers don't have to worry about it. They can treat the success and failure messages as they always have.

To alter the template, navigate to the shared components for the application, click on Templates and edit the page template that you’re using throughout your application. In most cases there will probably only be one template that you’re using, but for more complex applications there may be 2 or 3 you need to edit. You really only need to edit the templates that are used on pages you want to use the growl notification. In my case, there was just one.

Once you edit the template, you’ll need to scroll down to the Subtemplate region. This is where the Success Message and and the Notification (Fail) message templates are defined.

In the Picture below you’ll see the code in both regions that replaced the original code included in the template.

PastedGraphic-2011-06-23-15-24.jpg

In both cases, the first thing the code does is create a DIV, giving it an ID that we can grab ahold of later and sets its CSS style to “display:non”. This will keep the DIV from displaying until the Gritter script styles it. The contents of the DIV is the appropriate message template replacement variable, #SUCCESS_MESSAGE# and #MESSAGE#.

The next bit of code is a call to a piece of JavaScript code that we’ll talk about in just a moment. The first call, to growlSuccess takes two parameters. First is the ID for the DIV so the script can get a handle on it, and the second is the duration for how long the notification should be displayed. In this case, 5000 is about 5 seconds. The second call, to growlSticky only takes one parameter, the ID of the DIV. This function creates a “sticky” notification that the user will need to dismiss. The reason I created this one is because you want to make sure the end user sees and acknowledges the any error message.

The last piece of the puzzle is the javascript that kicks off the Gritter script. Again, I chose to implement this on Page Zero.

Region Name: GRITTER - JavaScript
Region Type: HTML Text
Template Type: No Template
Display Point: After Header

And the code in this region is as follows.

<script type="text/javascript">

function growlSuccess(vRegion, vLength){
// Get the message out of the DIV created by the page template
var vMessage = document.getElementById(vRegion).innerHTML;
// Instantiate the gritter message.
$.gritter.add({
// (string | mandatory) the heading of the notification
title: 'SUCCESS',
// (string | mandatory) the text inside the notification
text: vMessage,
// (bool | optional) if you want it to fade out on its own or just sit there
sticky: false,
// (int | optional) the time you want it to be alive for before fading out
time: vLength
});
return false;
}

function growlSticky(vRegion){
// Get the message out of the DIV created by the page template
var vMessage = document.getElementById(vRegion).innerHTML;
// Instantiate the gritter message.
$.gritter.add({
// (string | mandatory) the heading of the notification
title: 'MESSAGE',
// (string | mandatory) the text inside the notification
text: vMessage,
// (bool | optional) if you want it to fade out on its own or just sit there
sticky: true,
// An image to place inside the gritter notification
image:'#IMAGE_PREFIX#menu/info_32.gif'
});
return false;
}

</script>

This is the definition of the two functions that we called in the template code. The comments in the code should document what it’s doing fairly well. But the main thing to understand are is the call to gritter.add. This is the “magic” code that creates the Gritter “floating message”.

Referencing the Gritter documentation, you’ll see that there are quite a number of options that I’m not using. I wanted to keep things fairly simple just to make sure I wasn’t fighting against complexity in the early stages. I did go an extra step and put an image inside the sticky message type that is used for Notifications, to differentiate it from the Success Message.

Here’s the end result...

PastedGraphic1-2011-06-23-15-24.jpg

Hopefully this not only answers the question, “Is it possible”, but also shows you that there is way more to APEX than meets the eye. You can use JavaScript in a number of places that you wouldn’t expect. I’m continually pleasantly surprised at the flexibility of APEX.


EDIT (6/24/11) - Patrick Wolfe, completely independently, has created a Dynamic Action plugin that does much the same thing (see the demo here). The reason I went down this path is because the DA Plugin isn’t easily implemented for the APEX Success and Failure message. Anyway. Interesting that two people solved a strikingly similar problem and came up with much the same solution!



5 comments

  1. Unknown on June 23, 2011 at 8:14 PM

    Very cool!

     
  2. Bhavin Adhvaryu on June 27, 2011 at 5:58 AM

    Hi Doug,

    Thanks very much for your Growl style notification blog. I like your idea and love to implement in my appliaction.

    I have tried impementing the solution as you mentioned in your blog but I can't see any growl.

    I have put the js/css in GRITTER - Includes region & javascript code too in GRITTER - Javascript region on page 0 (Zero). Updated the main template as you mentioned.

    I have used -- href="WORKSPACE_IMAGES#jquery.gritter.css" and src="WORKSPACE_IMAGES#jquery.gritter.min.js" on page zero as my application is on oracle apex hosted site.

    Can you please help me to sort out this issue?

    Kind Regards,
    Bhavin

     
  3. Doug Gault on July 1, 2011 at 11:01 AM

    Bhavin,

    If the code that you put in your comment is directly cut and pasted from your app, then you're missing the beginning # . It should read.

    href="#WORKSPACE_IMAGES#jquery.gritter.css"
    src="#WORKSPACE_IMAGES#jquery.gritter.min.js"

     
  4. Oscar E. Duarte O. on October 11, 2012 at 9:21 PM

    Great Idea, can you give us idea but using pluggin notification, it is loaded into app so, it has a js files. Is Possible? How wil be?
    Thanks for your Knowledge.

     
  5. Doug Gault on October 12, 2012 at 8:29 AM

    Oscar,

    The oracle team has already created a Dynamic Action plugin that implements this functionality. If you look here:

    http://www.oracle.com/technetwork/developer-tools/apex/application-express/apex-plug-ins-182042.html#dynamic

    You'll see the NOTIFICATION plugin that does something that is VERY Similar to what I blogged about.

    The plugin includes all the PL/SQL and Javascript necessary and uploads them into the page when necessary, so you don't have to worry about doing this.

    Hope this helps.

    Doug