Within ADF Faces we take much goodness and added value for granted. One such feature came to my attention just the other day. Had you noticed that when you have several errors outstanding on the screen the framework gives you a hyperlink to set focus to that field? See the error on commissionPct below and the hyperlink that you could click to move focus to it in the screen:
So that's neat and I must confess that although I must have stared this one in the face hundreds of times I never really groked how cool that was.
Anyway, as is usual, this was not just a random act of attention on my part but rather considering a couple of different user cases which both boiled down to the basic question. Can we extend these error dialogs in some way to show more detail or carry out some other action in response to the error? So that's what I wanted to work through in this article, because the answer is (of course) yes!
How to make the Messages Interactive?
So let's take a look at how to do this. The clue is in the documentation for the tag which casually mentions that you can include HTML into your Faces messages or message detail. and one of the examples is the embedding of an anchor tag to embed a hyperlink - something that could indeed be useful in a message. So it occurred to me that this hyperlink provided the opportunity for an actionable gesture that the user could make to do stuff within the actual application as well as jump off to another one. However, it's not all plain sailing, you can't just embed any old HTML markup into the messages (I did try!) So no embedding of buttons, and even for an <a> tag you can't wire in JavaScript with an onClick=, that all get stripped out.
However, with a carefully shaped href attribute you can call JavaScript. So for example I've crafted the following message detail string:
<html>Detail for errror <a href=javascript:errorDetailCallback('it1','d1');>here</a></html>
This renders the word "here" as a link in the message and then when the user clicks on that it calls a JavaScript errorDetailCallback() with a couple of arguments. This function is included in a script linked onto the page using a standard <af:resource> tag.
The script itself could do anything. In my sample I'm actually making it call back to the server (via an <af:serverListener> defined in the document. Here's the JavaScript function:
function errorDetailCallback(errorContext, documentComponentId){ var callbackComponent = AdfPage.PAGE.findComponentByAbsoluteId(documentComponentId); if (callbackComponent != null) { var serverEvent = new AdfCustomEvent(callbackComponent, "errorDetailEvent", {detailKey : errorContext}, true); serverEvent.queue(true); } else{ AdfLogger.LOGGER.severe("errorDetailCallback: Error unable to locate document component: " + documentComponentId); } }
The custom event is wired into the server via the <af:serverListener> tag:
<af:serverListener type="errorDetailEvent" method="#{indexPageBB.errorDrilldownHandler}"/>
The server side method (errorDrilldownHandler()) in my case gets the custom event and then shows a popup in response:
public void errorDrilldownHandler(ClientEvent clientEvent) { Map params = clientEvent.getParameters(); String detailKey = (String)params.get("detailKey"); if (detailKey != null && _errorDetail.containsKey(detailKey)){ _lastErrorKey = detailKey; } else{ _lastErrorKey = "UNKNOWN"; } //Now do what you want. In this case show a popup with a detail message RichPopup.PopupHints hints = new RichPopup.PopupHints(); getDetailPopup().show(hints); }
Note that in the above case, the _lastErrorKey is simply a variable which is used by the getter that populates the text in the popup, it's used as a key into a Map with some further message details in it. However, what you do in this callback routine is of course up to you.