I am very flattered to find out that not only is my x$ OpenNTF xSnippet being used more widely than I realized (over 600 downloads). It now being used in the latest release of the OpenNTF Extension library.
//jQuery selector that works with XPages IDs
//See - http://openntf.org/XSnippets.nsf/snippet.xsp?id=x-jquery-selector-for-xpages
function x$(idTag, param){
idTag = idTag.replace(/:/gi, "\\:") + (param ? param : "");
return($("#" + idTag));
}
Who knew my first foray into the XPages community would have such an impact.
The lesson here boys and girls should be that you should *share* your work however small and insignificant you think it is. Like all scientific research, very little code is “a completely new way of doing things”. Generally everything grows incrementally and just because you don’t think something is significant, someone else might.
You are reading this – what have you shared recently?
You now have a re-usable custom control which can be added to any of the XPages in your database.
Creating your XPage
Create a new XPage and create a new Domino Document data source from one of the forms in the database.
Drag and drop your fields to the XPage
Drag and drop the ccMaskedInput custom control onto your XPage
Add the following script block to the bottom of your XPage/custom control and change the fields names to match your field names. (Change the masks to match the masks you want on your form)
<xp:scriptBlock id="scriptBlock1">
<xp:this.value><![CDATA[
jQuery(function($){
x$("#{id:date1}").mask("99/99/9999"); //change the field name
x$("#{id:phone1}").mask("(999) 999-9999"); //change the field name
x$("#{id:SSN1}").mask("999-99-9999"); //change the field name
x$("#{id:custom1}").mask("99-99-aaaa-9"); //change the field name
});
]]></xp:this.value>
</xp:scriptBlock>
And it’s as easy as that !
In a Nutshell
The Input Mask code:
Adds the programmed “Mask” to the field as the page loads
Tracks what you type and how many characters you have added so far
Checks what you type and matches it against the allowable character list for that character at that position in the string
Updates the mask accordingly and moves past the “Mask” characters is applicable.
This week we are going to look at a Masked Input plugin which provides another easy to implement but big impact to the users interface improvement capability.
The Demonstration
Click here to see a demonstration of the Input Mask plugin running within an XPage
The Sample Database
Rather than create a new URL every week I am going to stick with the same one from now on. It will contain a conglomeration of all the previous weeks examples and is basically a copy of the demo database on the website. The jQuery in XPages Sample Database
Increases user buy in by adding a visually appealing enhancement to the XPage
Allows the developer to pre-define the input order and type of keyboard characters
Shows the user what the application expects them to input
Enforces validation rules by preventing users from typing in potentially incorrect values
Input Mask example
The Input Mask adds the (__) ___-____ x_____ into the field and as the user starts to type the “Mask” is replaced by the keyed value.
Input Mask example user input
If the user attempt to try and enter anything other than a number nothing happens. This effect is very easily implemented using only a few lines of JavaScript to instantiate the code. Remember in XPages we have to use the x$ functionand the {id:} name of the field to select the real id in the HTML Document Model (DOM)
The Input mask plugin API comes with an abundance of other features you can change like:
Changing the mask character from _ to something else
Triggering a function once the mask is complete (all characters filled in)
Supplying your own mask definition (i.e if you want a specific character other than a-zA-Z0-9)
Allowing for wildchard characters (you don’t care what the user types)
Allowing for optional characters – (option phone extension or 4 final characters on a 9 digit zip code)
Adding this plug in to your database will take less than 5 minutes and you can improve the user interface immediately. Check out The Masked Input website for more information and futher information on the API parameters/capabilities.
Note:When you are submitting the form you will still need to validate it as the Input Mask does not handle validation outside of the field itself. The whole field value is submitted (Input Mask value as well) so if you create a (123) 123-1234 mask you will need to make sure you can accept that on the back end in a text field and you don’t need a 9 digit number.
For more information on how to add this capability to your XPages check out the “how does it work” article
// --- hijack dojo XHR calls
dojo._xhr = dojo.xhr;
var loadOld;
function hijacked( response, ioArgs ){
alert( response ); // change code here to do whatever you want. //
loadOld( response, ioArgs ); // call the original function
}
dojo.xhr = function( mode, args, bool ){
loadOld = args["load"];
args["load"] = hijacked;
dojo._xhr( mode, args, bool );
}
I was able to intercept the ajax call being made to the server. As I previously discussed in the original article I knew that the response was blank but was unable to get a handle on it – now I have.
When using a date picker in an XPage, user’s can still type incorrect data into the field which requires validation when saving.
Solution
Don’t let them type anything into the field and provide the calendar picker when they click into the field.
When you turn an XPage Edit Box into a “date field” you can select to use the date/time picker popup as we see below
Creating a date field with picker
But when that translates onto the webpage, the user can still type in the field and enter any old rubbish, which then needs to be validated.
Users can still type rubbish into the date field
Avoiding unnecessary validation is good for two reasons:
Less user frustration when they get the input wrong
Less code to write
We are going to change the form dynamics so that:
When the user clicks in the field
The calendar popup will appear
When the user tries to type anything
Nothing will happen (so they can’t type anything)
We can achieve this by adding a simple function to the onClientLoad event of the XPage.
First we have to look at the created HTML for the date control in the webpage.
Date field container for the Edit Box and the Calendar Button
What we can decipher is that the XPage has created a <SPAN> container (amongst other things) around the Edit Box and the Calendar Button. However, we do not know the name of the function which is called when the button is clicked (it is dynamically created as part of the dijit widget).
We do not need to know the function name because we can programmatically trigger the onClick event of the button.
The following code works like this:
When the onFocus() event of the Edit Box is triggered the the onClick event of the calendar button is triggered
When the user tries to type in a value they are prevented from doing so
in the clientOnLoad event of your XPage or Custom Control add
improveDatePicker("#{id:contractTermDate1}")
and in a script library add the following
function improveDatePicker(idTag){
//Select the BUTTON within the container
var calButton = x$(idTag+"_Container", " .dijitButtonContents")
//Select the dateField
var dateField = x$(idTag)
//When focusing on the field click the button
dateField.focusin(function (){
calButton.click()
})
//When pressing a key in the field
dateField.keypress(function(evt){
//prevent the event from completing
evt.preventDefault()
})
}
Today marks the release of my first video for Notes in 9. It has been my absolute pleasure to work with David Leedy on this project and I really hope that people find it useful. This blog entry is intended for my side-notes and also to provide a spot for any questions/comments.
This came about after my blog article on Adding a “working” visual indicator to the XPages TypeAhead. David approached me because he thought it would make a great Nin9 video. The original article was written from code on an 8.5.2 server and when I went to make the video on my 8.5.3 server I found that the HTML code generated for the type ahead was different any my code was “broken”.
As i said at the start of the original article, this is not bullet proof because you are always at the risk of this happening because we are manipulating the HTML created by the server. When IBM release 8.5.3 they upgraded dojo to 1.6 from 1.4 and the way the dijit comboxbox works had changed from one version of dojo to another, so IBM had to adapt.
The code in the example will work for 8.5.3 and 8.5.2 however because of the beauty of using the dojo.query. The 8.5.2 version is less complex and a couple of the lines in the addVisual() function are irrelevant. Because we are working with the dojo.query selector, and it find nothing to action on in the 8.5.2 HTML code, it does nothing – and most importantly fails over gracefully!!
This how does it work article is a follow on from the original jQuery in XPages #1 – Highcharts and goes into depth as to how the functionality was created and how you can modify it yourself.
Be fore-warned this is quite dry and not for the faint of heart, but hopefully will provide a good insight and understanding.
How does this work?
This Highchart demonstration is made up of the following design elements
To avoid making this article long(er than it already is) I am going to focus just on the drawHighchart custom control and the Highchart theme. The sample database is available for download and you can pick it to pieces at your leisure.
Highcharts Theme
The theme works by:
Extending the oneuiv2 theme (as a demonstration that it does not conflict)
Imports the jQuery library we need (jquery-1.7.1.min.js)
Imports the highchart.js library. Because this is a pure JavaScript library there are no associated image files (amazing really)
This custom control is intended to be self contained for the sake of this demonstration and can be dropped onto any XPage in your application. The example has a dropdown for theme and graph type choices. These parameters are fed into the Highchart.js file based on the selected field values, but in your application where you only want a specific chart type they could be hard-coded. Later in the article you will see where the parameters are fed into the highchart.js library and how you can configure them yourself.
The code for the custom control breaks down like this:
Both the chart theme and chart type fields have a full refresh set on the “onchange” event. This reloads the page and data when a change is made. Again for the sake of a demonstration this is ok, but in your real-world application you would only provide one chart type and remove these drop downs.
NOTE: because the code is instantiated in the $(document).ready(function() that means a partial refresh will NOT work
Full update onchange event
The onClientLoad event of the custom control contains all the JavaScript which makes this work
onClientLoad event of the custom control
The JavaScript is shown below and performs two basic tasks
It determines the selected theme and calls a function in the jsFormFruit library which sets the theme
e.g. callGray()
It instantiates the Highchart and passes the chart type as a value derived from the XPage
“#{id:viewPanel1}” is the idTag for the viewPanel <—change this if your view panel is called something different
“#{javascript: getComponent(‘chartType’).getValue()}” is the value chosen for chart type
$(document).ready(function() {
var sTheme=x$('#{id:theme1}').val() //get the value from the theme dropdown on the webpage
switch(sTheme)
{
case 'gray':
callGray()
break;
case 'darkBlue':
callDarkBlue()
break;
case 'grid':
callGrid()
break;
default:
""
}
htmlTableGraph("#{id:viewPanel1}", "#{javascript: getComponent('chartType').getValue()}")
})
jsFormFruit JavascriptLibrary
The callGray(), callDarkBlue and callGrid() functions are all very similar and while it might look like a lot, it is actually very readable and you can see how the color scheme is created. NOTE : this is all JavaScript and can therefore be changed to suit your own needs – no images!! This is a snippet and the whole code is available in the database in the script library
The htmlTableGraph is my modified version of the example given at the Highcharts website. I had to modify it because the viewPanel table created by XPages is not the same as the simple example given. Here’s the breakdown……
function htmlTableGraph(idTag, sType){
/**
* Visualize an HTML table using Highcharts. The top (horizontal) header
* is used for series names, and the left (vertical) header is used
* for category names. This function is based on jQuery.
* @param {Object} table The reference to the HTML table to visualize
* @param {Object} options Highcharts options
*/
Highcharts.visualize = function(table, options) {
// the categories
options.xAxis.categories = [];
$('tbody tr td:first-child', table).each( function(i) {
options.xAxis.categories.push(this.lastChild.innerHTML);
});
// the data series
options.series = [];
$('tr', table).each( function(i) {
var tr = this;
$('th, td', tr).each( function(j) {
if (j > 0) { // skip first column
if (i == 0) { // get the name and init the series
options.series[j - 1] = {
name: this.lastChild.innerHTML,
data: []
};
} else { // add values
options.series[j - 1].data.push(parseFloat(this.lastChild.innerHTML));
}
}
});
});
var chart = new Highcharts.Chart(options);
}
//idTag is the viewPanel1 id passed into the function
var table = document.getElementById(idTag),
options = {
chart: {
renderTo: 'container',
type: sType
},
title: {
text: 'Data extracted from a HTML table in the page'
},
xAxis: {
},
yAxis: {
title: {
text: 'Units'
}
},
tooltip: {
formatter: function() {
return '<b>'+ this.series.name +'</b><br/>'+
this.y +' '+ this.x.toLowerCase();
}
}
};
Highcharts.visualize(table, options);
}
The HTML created by the XPages server
The HTML generated by the XPage to display the viewPanel looks like this where every table row TR has three table cells – inside each TD cell there is a span and inside that a value
options.xAxis.categories = []; //sets up the array into which we will add the xAxis (the fruit)
We will work right to left to read what is going on
$('tbody tr td:first-child', table)
within the table (viewPanel1) element select everything which matches
the first td (td:first-child)
inside the tr
inside the tdbody
.each( function(i) {
because we can chain jQuery functions together we are cycling through each of the selected elements and for each element we are going to execute the function
.lastChild (the last DOM element within the TD) – this bypasses the span within the TD
.innerHTML – the HTML within the element
options.xAxis.categories.push (add (push) the value into the options object on the xAxis.categories value)
Simply put this 4 lines of code cycles through the table, extracts the fruit values and adds them to an array for display on the xAxis (Apples, Oranges etc)
Parsing the table values
options.series = [];
$('tr', table).each( function(i) {
var tr = this;
$('th, td', tr).each( function(j) {
if (j > 0) { // skip first column
if (i == 0) { // get the name and init the series
options.series[j - 1] = {
name: this.lastChild.innerHTML,
data: []
};
} else { // add values
options.series[j - 1].data.push(parseFloat(this.lastChild.innerHTML));
}
}
});
});
options.series = [];
Creates the array for drawing the chart values
$('tr', table).each( function(i) {
});
For each TR within the table (viewPanel) provided cycle through each row
i is the counter here as to which row we are on going down the table
var tr = this;
$('th, td', tr).each( function(j) {
});
for every th or td in each tr
j is the counter here as we are going across the row
if (j > 0) { // skip first column
if (i == 0) { // get the name and init the series
options.series[j - 1] = {
name: this.lastChild.innerHTML,
data: []
};
} else { // add values
options.series[j - 1].data.push(parseFloat(this.lastChild.innerHTML));
}
}
We ignore the first column (xAxis)
if j > 0 (skiping the first column which is the xAxis (apples) values)
if i = 0 (we are on the first row (the top one) and therefore we have the name Jane and John
add this (tr).lastChild.innerHTML (the HTML within the span) to the NAME value within the options.series array (NO DATA)
else (we are not on the top row) add the innerHTML value (converted to a number with parseFloat) to the options.series array
This section creates a string which looks like this – that is then parsed by the highchart.js
so therefore we need to select by name (which I don’t like as it could be ambiguous, but have no choice) you have to select the radio button group by name (don’t use x$). In this case we are testing to see if “Yes” has been selected – No or no selection at all will fail.
if ($("[name$=CertNextYearNeeded]:checked").val() != 'Yes') {
msg="Select the option\n";
}
[name$=CertNextYearNeeded] <— Selects all elements with a name like *CertNextYearNeeded
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
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