TypeAhead.js – making it readable and “why dojo?” I get it!

LOVE THIS

I wanted to break into the TypeAhead.js library which XPages uses to make the type ahead functionality work

looks like this….uuuuurgh

TypeAhead.js minified
TypeAhead.js minified

Quick search online and I found http://jsbeautifier.org/ and when I took the code and pasted it in look what happened – LOVE IT

readable TypeAhead.js
readable TypeAhead.js

And as soon as I look through this I get why IBM went with dojo. It is not about the pretty interface (which jQuery wins hands down) it is about the programming framework.

The provide, declare, hitch capabilities within dojo allow for class object creation with locally scoped variables and many other cool things. jQuery does not provide a framework for this, it doesn’t try to either. You can always create class objects manually and that is what people write around their jQuery. Dojo provides the class framework directly and the TypeAhead.js is a fantastic piece of work. One of these days when I have nothing better to do I will break down how it works.

x$ update for dojo

Problem
dojo selector –  “dojo.query” doesn’t work for elements generated by xpages

Background

This is a follow on to the jQuery selector function for xPages – x$(“#{id:inputText1}”) article I wrote previously.

Solution

Turns out that dojo.query suffers from the same problem:  dojo.query cannot understand elements with colons (:) in it. We have to convert the idTag of the element to escape the colons with a backslash.

  • dojo.query(“view:_id1:inputText1”).style(“border”, “1px solid red”) – will not work
  • dojo.query(“view\:_id1\:inputText1”).style(“border”, “1px solid red”) – will work and is converted using the function

So I have updated my x$ function to cover dojo and jQuery


function x$(idTag, param, jd){ //Updated 28 Feb 2012
      idTag=idTag.replace(/:/gi, "\\:")+(param ? param : "");
      return( jd=="d" ? "#"+idTag : $("#"+idTag));
}

the param and jd parameters are optional (in fact jd is only necessary if you are using dojo)

Interesting Note

For the uninitiated you will note that jQuery returns a $() rather than a string – this is because it is actually returning a jQuery Object. That is a fundamental difference in the way that dojo and jQuery function as code libraries.

Examples

here is an example code of how it would be used in dojo or jQuery


//dojo
dojo.query(x$("#{id:inputText1}", "", "d")).style("border", "1px solid red")

//jQuery equivalent

x$("#{id:inputText1}").css("background-image", "1px solid red")

An example is shown below

<?xml version="1.0" encoding="UTF-8"?>

<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

<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));
 }
 ]]>
 </xp:this.value>
 </xp:scriptBlock>
 <xp:inputText id="inputText1"></xp:inputText>
 <xp:br></xp:br>
 <xp:br></xp:br>
 <xp:button value="Change Border (Fail)" id="button2">
 <xp:eventHandler event="onclick" submit="false">
 <xp:this.script><![CDATA[dojo.query("#{id:inputText1}").style("border", "1px solid red")]]></xp:this.script>
 </xp:eventHandler>
 </xp:button>
 <xp:br></xp:br>
 <xp:br></xp:br>
 <xp:button value="Change Border X$" id="button1">
 <xp:eventHandler event="onclick" submit="false">
 <xp:this.script><![CDATA[dojo.query(x$("#{id:inputText1}", "", "d")).style("border", "1px solid red")]]></xp:this.script>
 </xp:eventHandler>
 </xp:button>
</xp:view>

Adding a “working” visual indicator to the XPages TypeAhead

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

Normal functionality of an XPages type ahead
Normal functionality of an XPages type ahead

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.

Breaking down the type ahead field
Breaking down the type ahead field

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}");
Adding addVisual to the onblur and onchange events
Adding addVisual to the onblur and onchange events

Removing the image 

In the onblur and onchange events we add the following to our XPage

    removeVisual("#{id:travelLocator1}");
Adding removeVisual to the onblur and onchange events
Adding removeVisual to the onblur and onchange events

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 🙂

Type Ahead with Visual Indicator
Type Ahead with Visual Indicator

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)
The code
<?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

1000 hits in the first month :)

Today I hit the 1000 mark for the new blog, not bad for only being up a month. I have lots more to write about and unfortunately in between the writing, work is something I have to do to pay the mortgage….thank you to everyone who’s visited and hopefully this will grow to become a better resource for the community 🙂

 

 

Using jQuery to add table interactivity to your XPage

Very quickly and easily we can create a pretty good looking data view as an XPage – drag and drop a view control onto your page and your off in under 60 seconds – very simple, very functional.

Simple XPages view of data
Simple XPages view of data

However, for many users though (especially “older” corporate users, who are usually the ones making decisions on your projects and paycheck) it is simply not enough to be “functional”, it needs to be EASY. For example:

  • On a large table it is difficult to scan across from one side to the. Especially as screens get larger and resolutions get higher we are putting more and more data onto a page
  • Which link opens the data set? The only indication we have is the blue link on the left – not obvious to all

User’s expectations are based on their experience with the internet and everyday that gets higher and higher. As developers we are always looking for ways to make our pages look snazzier and also easier to user at the same time.

Here are some simple tricks using jQuery and my x$ function.
Add all the following to the onClientLoad event of the XPage.

  1. To make the table rows alternating colors :
    The jQuery is selecting every “even row” within the viewPanel and applying the “alt” class to it

    x$("#{id:viewPanel1}", " tr:even").addClass("alt")
    
    Adding Alternating Lines using jQuery
    Adding Alternating Lines using jQuery

     

  2. To make the table rows highlight with a mouseover
    The jQuery adds a listener to each row in the viewPanel1 which toggles the “active” class when the row is hovered over.  I also added a hand pointer to make it look selectable.

    x$("#{id:viewPanel1}").delegate("tr", "hover", function(){
        $(this).css('cursor', 'pointer' ).toggleClass("active");
    });
    
    Adding a mouseover color to a table row using jQuery
    Adding a mouseover color to a table row using jQuery

     

  3. Make the entire row selectable with the 1st column’s href 
    The jQuery is selecting the “href” on the “first a” that it finds in the row and is binding it to the click event of the whole row. This is currently going to open a new page.
    Note: ” tr:gt(0)” selects all rows index greater than 0, that ignores the title row at the top which does not have the links in we are looking to use

    x$("#{id:viewPanel1}", " tr:gt(0)").bind('click', function(){
        window.location = $('a:first',this).attr('href');
    });
    
    Binding the first anchor tag to the whole row using jQuery
    Binding the first anchor tag to the whole row using jQuery

    And we could stop there – 7 lines of code and we have made major improvements to the data table. But to complete the transformation we will bring the data to the user rather than opening up another window.

  4. Make the page link appear in a nice looking dialog
    The jQuery is opening a dialog box on the page and displaying the content using the url of the row. Having to click back and forth from information is distracting and tedious. This way it helps the user never leave the page of information they are looking for. This is a little more complicated, but not really if you step through it.

    • We bind to the click event of the first row
    • We trap the page offset of the row which was clicked.
    • We open the travelDetails dialog (where id=travelDetails is just a blank label at the bottom of the page)
    • The position of the dialog, the height, width and the fact that it is modal are set.
    • As we saw before we can get the anchor href from the first column and with that we ajax in the contents of that form
    • The HTML returned is then inserted into the travelDetails and displayed to the user.
    x$("#{id:viewPanel1}", " tr:gt(0)").bind('click', function(e){
      var offset = $(this).offset();
      e.stopPropagation();
      x$("#{id:travelDetails}").dialog({
        close: function(event, ui) {
        $(this).empty();},
        position: [100, (offset.top)-100-$(window).scrollTop()],
        resize: false,
        width: 400,
        height: 250,
        modal: true
      });
      x$("#{id:travelDetails}").dialog('open');
      $.ajax({
        type: 'GET',
        url: $('a:first',this).attr('href'),
        context: document.body,
        success: function(html){
          x$("#{id:travelDetails}").html(html)
        }
      });
    });
    
Displaying the travel details in a simple dialog with jQuery
Displaying the travel details in a simple dialog with jQuery

This process is just as applicable to any normal Domino webpage (non-XPage) with an embedded view in it.

 

Update 1 March 2012

I have been asked to add the style sheet which you will need to add to see the effects. Ideally you should add it to a Stylesheet and add it to the XPage as a resource, but for the sake of demonstration just copy and paste this into the source code at the top under the <xp: view tag

<style>
.alt{ background: #ecf6fc; }
.active { color: red; background:#CCCCCC; }
</style>

Why corporate developers make sucky corporate websites

Requirement

“I want an easy way for customers to view their data and access the information with the least amount of effort.”

What the corporate developer heard me say

“blah blah…..least amount of effort”

What the independent contractor heard me say

I am not paying a lot of money for it

What I didn’t say but expect

“I want it to look amazing and be so simple to use my mother-in-law could use it”

 

Corporate developers and Independent contractors live in completely different worlds. Every consideration during a project life-cycle has a different perspective, the review process is different, the expectations are different.

And here’s some of the difference between independent contractors and corporate developers (in my experience).

  • Corporate developers….
    • are secure in their job status
    • are rarely visionaries, if they were they would get extremely frustrated and become an independent contractor
    • are almost guaranteed to have to develop for only one (usually old) version of Internet Explorer
    • will move onto today’s fire-fighting task in the middle of trying to meet their deadlines and not be able to focus on smoothing the edges (look and feel) on their application
    • will always have the high ground when it comes to requirements being met and it was the customers’ fault for not being more specific in their look and feel requirements
    • will create functionality first and worry about user experience last (if they have time)
    • don’t much care if their application is used or not because there is another fire coming along tomorrow
    • are going to get paid if the project fails
  • Independent Contractors
    • have a specific task which they were hired for
    • must go above an beyond if they want to continue to get (re-)hired
    • know that the user experience will be the first thing they are judged on regardless of functionality
    • stake their reputation on the quality of their work
    • will never have the high ground – regardless of whether or not they are right
    • definitively care if their product gets used – they need the reference for a job well done
    • will not necessarily get paid if the project fails
    • are often on the bleeding edge of technology because they need to differentiate themselves from the pack
    • are often expected to be able to program for all browsers, with no additional cost overhead.

 

Corporate developers make sucky websites because of their environment. Unfortunately Domino is a corporate tool and I believe there is a correlation.