I arrived at this in as much a lesson in learning how the dojo and dijit interface works in the browser. This is a neat little demonstration but I am not sure that it is rock solid in terms of usage. Because it is manipulating the DOM on the front end, it always runs the risk of breaking something in a future release of dojo or XPages. But anyway here we go.
Problem
No user feedback that a type ahead field is looking up data
Background
The normal XPages type ahead functionality looks like this
Unfortunately there is no visual feedback to the user, they have no idea if it is a type ahead and they have no idea if anything is happening……This is especially irritating when the network is slow
Breaking down the the HTML
So we are going to add a visual indicator to the field – here’s how we break it down:
The “field” that we see is actually a mixture of divs and fields created by dojo and the Xpages server – breaking it down here with FireBug we can see that the field is made up of 8 (EIGHT!) DIVs and the actual input field. I have highlighted the sections we are interested in.
You may have seen the right hand DIV being used in form validation – a warning icon is displayed at the end of the field – pretty slick modification of the interface really.
You will also see that there is a whole world of style and class pain and suffering going on to make this happen. We really want to stay clear of altering anything more than we have to and that is why we are going to focus in on the right hand DIV in the above picture
Selecting the object to manipulate
The section we are interesting in manipulating is the smaller inside DIV shown above
<div class="dijitReset dijitValidationIcon" style="visibility: hidden;"> <br> </div>
and this is an element within the <div id=”widget_view:_id1:travelLocator1″ (Highlighted in the picture above)
Using dojo we can “select” this class using the following code
dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d"))
This generates the following code – selecting all elements with the class .dijitValidationIcon, within the widget_view:_id1:travelLocator1 element
dojo.query("#widget_view:_id1:travelLocator1 .dijitValidationIcon")
oh yeah I had to make an update to x$ as well – turns out that dojo can’t query items with colons (:) in it any better than jQuery can. All the code is at the end of this article and I will post another entry showing the update in more detail. Suffice as to say at this point that I have to change widget_view:_id1:travelLocator1 to widget_view\:_id1\:travelLocator1 for the selector to work correctly.
Manipulating the object
We can now create the following functions to add/remove the visual effect to the field
I added the jQuery equivalent(s) for two reasons, 1) that’s how I figured it out in the first place and 2) it highlights how much easier IMHO selectors are in jQuery).
The passed in parameter idTag will come from the CSJS we are going to add to the events in the designer client. This function selects the DIV we wish to manipulate, changes the background image then finally makes it visible (by default it is hidden)
function addVisual(idTag){ var newImage="url('/dojo130/dijit/themes/tundra/images/treeExpand_loading.gif')" dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")).style("backgroundImage", newImage) dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")).style("visibility", "visible") //jQuery equivalent //x$("widget_"+idTag, " .dijitValidationIcon").css("background-image", newImage) //x$("widget_"+idTag, " .dijitValidationIcon").css("visibility", "visible")</pre> }
The equivalent code to remove the image (when we are finished) is as follows (removing the image and hiding the DIV)
function removeVisual(idTag){ dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")).style("backgroundImage", "") dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")).style("visibility", "hidden") //jQuery equivalent //x$("widget_"+idTag, " .dijitValidationIcon").css("background-image", "") //x$("widget_"+idTag, " .dijitValidationIcon").css("visibility", "hidden") }
Adding the events to the XPage
The type ahead is triggered on the onkeypress event of the input field and the typeahead results are removed either on the onblur event clicking away from the field) or the onchange event (clicking one of the results).
Adding the image
In the onkeypress event we add the following to our XPage.
addVisual("#{id:travelLocator1}");
Removing the image
In the onblur and onchange events we add the following to our XPage
removeVisual("#{id:travelLocator1}");
Final result
This now changes our type ahead (tested in IE8, Firefox 7 and Chrome 17) to look like this. The visual indicator is added to the type ahead
No live demo
And this is the only kind of occasion where using WordPress blows – I do not have anywhere to do a live demo of this. Any volunteers to help me host examples like this would be GREATLY appreciated !!!
The code is listed below - you will have to change the following to work in your database
- your lookup view (avoiding 64K issues)
- the location of your dojo images
Known issues
- There is no indicator that NO results have been returned (that’s for another day)
<?xml version="1.0" encoding="UTF-8"?> <xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xp_1="http://www.ibm.com/xsp/coreex" xmlns:jQuery="http://www.jquery.com/xsp/coreex"> <xp:br></xp:br> <xp:table> <xp:tr> <xp:td style="width: 200px"> Travel Locator </xp:td> <xp:td> <xp:inputText id="travelLocator1" styleClass="title"> <xp:typeAhead mode="full" minChars="1" ignoreCase="true" htmlFilter="identity" preventFiltering="false"> <xp:this.valueList> <![CDATA[#{javascript:@DbColumn(@DbName(), "vwTravel", 1)}]]> </xp:this.valueList> </xp:typeAhead> <xp:eventHandler event="onblue" submit="true" refreshMode="norefresh" id="eventHandler2"> <xp:this.script> <xp:executeClientScript> <xp:this.script> <![CDATA[removeVisual("#{id:travelLocator1}");]]> </xp:this.script> </xp:executeClientScript> </xp:this.script> </xp:eventHandler> <xp:eventHandler event="onchange" submit="true" refreshMode="norefresh" id="eventHandler1"> <xp:this.script> <xp:executeClientScript> <xp:this.script> <![CDATA[removeVisual("#{id:travelLocator1}");]]> </xp:this.script> </xp:executeClientScript> </xp:this.script> </xp:eventHandler> <xp:eventHandler event="onkeypress" submit="false"> <xp:this.script> <![CDATA[addVisual("#{id:travelLocator1}")]]> </xp:this.script> </xp:eventHandler> <xp:eventHandler event="onblur" submit="false"> <xp:this.script> <![CDATA[removeVisual("#{id:travelLocator1}");]]> </xp:this.script> </xp:eventHandler> </xp:inputText> </xp:td> </xp:tr> </xp:table> <xp:br></xp:br> <xp:scriptBlock id="scriptBlock1"> <xp:this.value><![CDATA[ function x$(idTag, param, jd){ //Updated 28 Feb 2012 idTag=idTag.replace(/:/gi, "\\:")+(param ? param : ""); return( jd=="d" ? "#"+idTag : $("#"+idTag)); } function addVisual(idTag){ var newImage="url('/dojo130/dijit/themes/tundra/images/treeExpand_loading.gif')" //Change me for your server dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")).style("backgroundImage", newImage) dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")).style("visibility", "visible") //jQuery equivalent //x$("widget_"+idTag, " .dijitValidationIcon").css("background-image", newImage) //x$("widget_"+idTag, " .dijitValidationIcon").css("visibility", "visible") } function removeVisual(idTag){ dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")).style("backgroundImage", "") dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")).style("visibility", "hidden") //jQuery equivalent //x$("widget_"+idTag, " .dijitValidationIcon").css("background-image", "") //x$("widget_"+idTag, " .dijitValidationIcon").css("visibility", "hidden") } ]]></xp:this.value> </xp:scriptBlock> </xp:view>
Update – 5 March 2012
Like I said at the start of the article I did not know how robust this was and low and behold, the code above works in 8.5.2 but not 8.5.3.
Here are the updated addVisual() and removeVisual() which work in 8.5.3 and 8.5.2
function addVisual(idTag){ //8.5.3 version var newImage="url('/dojo130/dijit/themes/tundra/images/treeExpand_loading.gif')" //Change me for your server var inputField=dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")) inputField.style("backgroundImage", newImage) inputField.style("visibility", "visible") dojo.query(x$("widget_"+idTag, " .dijitValidationContainer", "d")).style("display", "block") inputField.attr("value","") } function removeVisual(idTag){ var inputField=dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")) inputField.style("backgroundImage", "") inputField.style("visibility", "hidden") dojo.query(x$("widget_"+idTag, " .dijitValidationContainer", "d")).style("display", "none") }
Update 22 March 2012
Through the help of Sven Hasselbach I have been able to add a fail icon – here is the article
