x$ – now a part of XPages Extension Library

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.

If you look here – http://bootstrap4xpages.com/xsp/.ibmxspres/.extlib/responsive/xpages/js/xsp-mixin.js and search for it you will find


//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?

🙂

 

Advertisements

jQuery in XPages #3 – Masked Input – How does it work?

Masked Input

This article is published in conjunction with the main jQuery in XPages #3 – Masked Input

 

How does it work?

To add the Masked Input capability to your XPages in a generic, re-usable manner you will need to do the following:

Adding the JavaScript libraries

In the designer menu select Window | Show Eclipse Views | Package Explorer

Opening the package explorer in Domino Designer
Opening the package explorer in Domino Designer

Search for the WebContent folder
On your local machine make a “js” folder with the following files in it

Drag and drop the js folder into the WebContent folder of your database

Drag and Drop your Masked Input js file
Drag and Drop your Masked Input js file

Creating the custom control to load the JavaScript libraries

Create the following ccMaskedInput custom control in your database

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

	<xp:this.resources>
		<xp:script src="js/jquery-1.7.1.min.js"
			clientSide="true">
		</xp:script>
		<xp:script src="js/x$.js"
			clientSide="true">
		</xp:script>
		<xp:script src="js/jquery.maskedinput-1.3.min.js"
			clientSide="true">
		</xp:script>
	</xp:this.resources>
</xp:view>

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.


jQuery in XPages #3 – Masked input

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

Masked Input

The Masked Input plugin by Josh Bush turns a plain editable field into a field which:

  • 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
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)

jQuery(function($){
   x$("#{id:date}").mask("99/99/9999");
   x$("#{id:phone}").mask("(999) 999-9999");
   x$("#{id:ssn}").mask("999-99-9999");
});

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

XPages TypeAhead v2.0 with added fail icon

Thanks to Sven Hasselbach who inadvertently gave me the information I needed to intercept the ajax call in dojo.

http://stackoverflow.com/questions/9776503/xpages-get-the-contents-of-a-this-partialrefresh-ajax-call

Using

// --- 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.

There is also a NotesIn9 video supporting this article

Check out the  demonstration (Thanks to David Leedy for hosting)

http://watchtower.notesin9.com/xomino/xPlay.nsf/xTypeAhead.xsp

xTypeAhead with new Fail icon
xTypeAhead with new Fail icon

Improving user interaction with the XPages date picker

Problem

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
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
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
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()
	})

}
Clicking in the improved date field
Clicking in the improved date field

Nin9 TypeAhead Demo

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.

Here’s a link to the video 🙂

Side Notes

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!!

Hope you enjoy the video 🙂

Marky

jQuery in XPages #1 – Highcharts – How does it work?

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

  • XPage
    • xFruit (the container XPage)
  • Custom Controls (where all the hard work is done)
  • Theme
    • Highchart (extends oneuiv2)
  • View
    • Fruit (data source for the viewPanel)
  • Form
    • Fruit (data container)
  • JavaScript Library
    • jsformFruit

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)

<theme extends="oneuiv2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="platform:/plugin/com.ibm.designer.domino.stylekits/schema/stylekit.xsd" >
	<resource>
		<content-type>text/css</content-type>
		<href>theme/style.css</href>
	</resource>
	<resource>
		<content-type>application/x-javascript</content-type>
		<href>jquery-1.7.1.min.js</href>
 	</resource>
	<resource>
		<content-type>application/x-javascript</content-type>
		<href>Highcharts/js/highcharts.js</href>
 	</resource>

</theme>

drawHighchart custom Control

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
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
    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
    • htmlTableGraph(“#{id:viewPanel1}”, “#{javascript: getComponent(‘chartType’).getValue()}”)
      • “#{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


function callGray(){
	Highcharts.theme = {
			   colors: ["#DDDF0D", "#7798BF", "#55BF3B", "#DF5353", "#aaeeee", "#ff0066", "#eeaaee",
			      "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"],
			   chart: {
			      backgroundColor: {
			         linearGradient: [0, 0, 0, 400],
			         stops: [
			            [0, 'rgb(96, 96, 96)'],
			            [1, 'rgb(16, 16, 16)']
			         ]
			      },
			      borderWidth: 0,
			      borderRadius: 15,
			      plotBackgroundColor: null,
			      plotShadow: false,
			      plotBorderWidth: 0
			   },
			   title: {
			      style: {
			         color: '#FFF',
			         font: '16px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif'
			      }
			   },
			   subtitle: {
			      style: {
			         color: '#DDD',
			         font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif'
			      }
			   },
			   xAxis: {
			      gridLineWidth: 0,
			      lineColor: '#999',
			      tickColor: '#999',
			      labels: {
			         style: {

			         etc etc etc.............
			         systematically going through each element and setting the style
			};

			// Apply the theme
			var highchartsOptions = Highcharts.setOptions(Highcharts.theme);
}

The htmlTableGraph() function

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

<tbody>
<tr>
<td>
    <span id="view:_id1:_id2:_id23:viewPanel1:0:viewColumn1:_internalViewText">
        Bananas
    </span>
</td>
<td>
    <span id="view:_id1:_id2:_id23:viewPanel1:0:viewColumn2:_internalViewText">
        1.0
    </span>
</td>
<td>
    <span id="view:_id1:_id2:_id23:viewPanel1:0:viewColumn3:_internalViewText">2</span>
</td>
</tr>

How we parse the generated HTML in jQuery

The first thing I want to discuss is this part of the function

		options.xAxis.categories = [];
		$('tbody tr td:first-child', table).each( function(i) {
			options.xAxis.categories.push(this.lastChild.innerHTML);
		});

Breaking it down section by section

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
options.xAxis.categories.push(this.lastChild.innerHTML);
  • this (the element we are acting on)
    • .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

options = {series: [{
			name: Adam,
			data: {1, 2, 2, 6 }
		},
		{
			name: Jane,
			data: 2, 0, 4, 9 }
		]
	}

And finally, once we have created all the data arrays – display the highchart

	Highcharts.visualize(table, options);