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
Update – Thanks to Tim Tripcony for pointing this out
If you are using 8.5.3 you can add the custom attributes to the field in the designer client. This removes the need to use the dojo.query
and added the javascript functions to a scriptblock at the bottom of the page
//function called when drag starts
function dragIt(theEvent) {
//tell the browser what to drag
theEvent.dataTransfer.setData("Text", theEvent.target.id);
}
//function called when element drops
function dropIt(theEvent, keepMe) {
//get a reference to the element being dragged
var theData = theEvent.dataTransfer.getData("Text");
//get the element
var theDraggedElement = document.getElementById(theData);
//add it to the drop element
if (keepMe){
//Add a clone of the element to the field - rather than move it
var newObj=dojo.clone(theDraggedElement)
theEvent.target.appendChild(newObj);
} else {
theEvent.target.appendChild(theDraggedElement);
}
//Add a new line for visual line up
var theBR=document.createElement("br")
theEvent.target.appendChild(theBR);
//instruct the browser to allow the drop
theEvent.preventDefault();
}
I changed the example slightly to add a clone capability rather than just drag and drop
Drag and Drop example 1 (Move)
Dragging to the first box “moves” the label
Drag and Drop example 2 (Clone)
Dragging the label to the second box create a copy of it using dojo.clone(node)
Conclusion
This is merely a prototype but demonstrates nicely the new and exiting capabilities of HTML5 without having the need for a dojo or jQuery library to do the drag/drop for you
So having made v2.0 of my TypeAhead control I am rapidly moving towards the next iteration making it easier to implement and not have to add functions to the fields in the designer. This becomes especially arduous if you have multiple TypeAheads in the same form and you want to add this capability to all of them.
The plan is to use a jQuery or dojo selector to get a handle on all the TypeAhead fields on the form and add the necessary function calls to each of them in one simple script library. (Because this is currently a capability without jQuery I am sticking with dojo, more verbose as it is)
So I created multiple TypeAhead fields (Copy/Paste) on the same XPage and looked at HTML to find out the selector I would need to create. I right click on the web page and view source and the out of the box HTML looks like this (pretty simple right..?):
So then I pulled up firebug to make sure I wasn’t being stupid, and sure enough I was…..it is not like I have already written about this before or anything….In firebug the actual DOM looks like this:
Right Click on the field and Inspect Element with firebug
Looking at the DOM within Firebug reveals Marky's stupidity
Oh DUH – it is a dojo widget (remember) and once the page is loaded, dojo does some major DOM manipulation to create the functionality. This is actually a really nice demonstration of how XPages and dojo work in tandem. XPages creates some fairly innocuous HTML and dojo tranforms it into considerably more complex and voluminous HTML.
So looking at the DOM we have a problem because the final field which is created (that I want to get a handle on) does not have anything indicating that it is a TypeAhead and how can I “select” this out without adding client side events in the XPage….
The Answer
The trick is to execute or “selector” before the dojo widget takes over and messes up the DOM. Instead of adding the code to the onClientLoad event (which is also when the dojo widget magic is triggered) we need to add it to a script block inside of the HTML – before the page has finished loading.
This is a subtle yet important timing detail during the rendering of HTML pages. You can add a script tag to an HTML page and have it execute before the page is rendered to the user, but it can only take action on what has already been “loaded”…this is also why dojo and jQuery are usually triggered when the HTML document is finally loaded – to make sure all the necessary fields are “loaded” on the page.
In this case we are going to add the script block right at the end of the XPage to make sure that everything we are looking for is “loaded” before we act on it. The script block is added at the bottom of the XPage after all the fields. This will execute before the onClientLoad event.
Adding an in-line script block at the end of the XPage
Selecting the elements before they are manipulated by dojo
But wait – there’s more to this that meets the eye….
What is also interesting to note is that the styling applied to the INPUT field before the dojo widget has it’s way with the HTML is retained….but it is NOT applied to the INPUT anymore..not even close…..
Don't trust the HTML
Because of this, I am not sure at this point if I am going to be able to add the timing handlers for the TypeAhead – it will be fun finding out though, watch this space….
This week’s article is about the visually effective jQuery plugin – labelify.js
Labelify.js
In the simplest terms we want to make our form look like the “label” is inside the field – here’s an example I am sure many of you are familiar with. The general concept is that instead of having the label adjacent to the field, the “Compose new Tweet” is inside the field itself and disappears once you click in the field.
Edit box with a "in-line" label
The demonstration
The demonstration can be found here – (demonstration)
The download
The sample jQuery in Xpages database (containing examples from all the articles) can be downloaded here – (download sample database)
How do we add Labelify to our XPages?
This effect works on an XPage where the labels and fields are generated by dragging and dropping the fields from a data source. How this works will be broken down later..
Adding fields to the XPages from a pre-defined data source
Here’s the steps:
Add the xLabelify.js script library to you database
Add the xLabelify.js Javascript library to your XPage as a Resource
This article is in support of the jQuery in XPages article #2 – Labelify.js and goes into depth as to how the functionality was created and how you can modify it yourself. How does this work?The Labelify example is made up of the following elements:
XPages
xLabelify (the example XPage)
StyleSheet
xLabel.css (look and feel)
JavaScript Library
xLabelify.js (the jQuery library creating the effect)
The xLabelify.js library is based on the original labelify.js created by Stuart Langridge. I have made a couple of modifications to the library to make it work in our XPages environment. Here is the breakdown of how it works……..
Extending jQuery
For more information on extending jQuery check out the (website). But in a nutshell, jQuery was envisioned to be extended so that anyone could use the basic jQuery selector to create a collection of objects in the DOM and then process them however they wanted.
In this case “labelify” is the extension and it is:
collecting all the $(“input”) elements on the web page and then
“.labelify” them with the text and labelledClass as parameters.”
(If we wanted to be more specific and wanted to select only the labels within a certain panel (rather than the whole page) or if we wanted to select individual elements by name/id we could do that too). The example below (using x$) selects just the fruit4 field.
I have annotated the code so that you can understand what makes this effect work. It is also a nice short look inside a jQuery extension. It is really quite elegant in its simplicity.
//extend jQuery with the function name "labelify" and it will receive the array called "settings"
jQuery.fn.labelify = function(settings) {
//"settings" will contain the variable title and labeledClass which will be created as jQuery objects
//by default the text: value will be title (if nothing is passed in at all)
settings = jQuery.extend({
text: "title",
labelledClass: ""
}, settings);
//lookups will be the default values pulled from the document model (DOM) at startup
var lookups = {
//The title: value will be taken from the title attribute of the input field being processed
title: function(input) {
return $(input).attr("title");
},
//The label: value will be taken from the label with a "for" attribute matching the id of the input field being processed
label: function(input) {
return $('label[for="' + input.id + '"]').text();
}
};
var lookup;
//$(this) refers to "this jQuery object" as opposed to "this" which in javascript is the current DOM element.
var jQuery_labellified_elements = $(this);
//for each labelified element ($(this).each)
//we are setting the lookup variable for each of the elements which are within "lookups"
//the value of lookup is going to be the text we are going to display within the field
//that has come from either the LABEL or the title attribute
return $(this).each(function() {
if (typeof settings.text === "string") {
lookup = lookups[settings.text]; // what if not there?
} else {
lookup = settings.text; // what if not a fn?
};
// bail if lookup isn't a function or if it returns undefined
if (typeof lookup !== "function") {
return;
}
var lookupval = lookup(this);
if (!lookupval) {
return;
}
// need to strip newlines because the browser strips them
// if you set textbox.value to a string containing them
$(this).data("label", lookup(this).replace(/\n/g, ''));
//When the user clicks or tabs into the field (focus event)
$(this).focus(function() {
//if the jQuery data (more on this further down) "label" is exactly the same type and value (===)
//Then the jQuery item (this).value equals the jQuery item defaultValue
//then remove the labelledClass
//in simple terms - if the user clicks into the field and the "labellify" text (e.g. enter a value) is displayed
//Then remove it from the field, hide it and put the defaultValue into the field (normally blank)
if (this.value === $(this).data("label")) {
this.value = this.defaultValue;
$(this).removeClass(settings.labelledClass);
}
//When the user clicks or tabs out of the field (blur event)
//IF the current value = the defaultValue (usually blank)
//Then put the labelified value (e.g. enter a name) back into the field
//Add the labelledClass (make it gray)
}).blur(function() {
if (this.value === this.defaultValue) {
this.value = $(this).data("label");
$(this).addClass(settings.labelledClass);
}
});
//This function removes all the labelify information from the field
//This function is called onSumit and means that if the deveoper validates for ""
//the field will contain "" onSubmit and will therefore not be validated as (enter a name)
var removeValuesOnExit = function() {
jQuery_labellified_elements.each(function() {
if (this.value === $(this).data("label")) {
this.value = this.defaultValue;
$(this).removeClass(settings.labelledClass);
}
})
};
//if the form is being submitted or the user has clicked onto another window (unload)
//then call the remove labelify function above and remove all traces of labelify from the field (jQuery $(this))
$(this).parents("form").submit(removeValuesOnExit);
$(window).unload(removeValuesOnExit);
if (this.value !== this.defaultValue) {
// user already started typing; don't overwrite their work!
return;
}
//MDR Mar 2012
//else clause added to resolve existing default values
//The function sets up the labelified field and says if the field is blank to start with insert the labelified information
if (this.defaultValue == "") {
// actually set the value
this.value = $(this).data("label");
$(this).addClass(settings.labelledClass);
} else {
//however in our XPages environment we often have default values in the field
//we might want to see the labelified version if the user blanks out the field
//but without this else clause the labelified value would be forced ontop of the default value
//so if the field is not blank then
//get the field value ("Cabbage" in our example")
var sTemp = this.value
//set the default value to blank (the code assumes Cabbage is the defaultValue)
this.defaultValue = ""
//create the data label within the jQuery object to be the labelify text (Enter a Vegetable)
$(this).data("label", lookup(this).replace(/\n/g, ''));
//put Cabbage back into the field
this.value = sTemp
}
});
};
jQuery object data
For more indepth information on jQuery.data check out the jQuery (site). But one of the constructs within the jQuery object is the .data capability. What jQuery does is it assigns data attributes to the DOM element and it keeps track of them with internal jQuery pointers. What this allows us to do programmatically is push and pull “data” from the jQuery object $(this) without having to define more DOM elements to contains the data. Creating DOM elements is an expensive transaction and pushing/pulling data from existing DOM elements is much more efficient. Although less of a transaction bonus, but still more efficient is that fact that we do not have to create new variables for each value either. This is not exactly what happens but you can imagine it conceptually – e.g. <input type=”text” value=”Marky” /> becomes <input type=”text” value=”Marky” data-label=”Enter a Vegetable” />
NOTE
Some interesting jsPerf tests have shown that in this case in some browsers jQuery is actually slower than setting attributes in the DOM manually. Still both are more efficient than creating variables in memory or new DOM elements via document.createElement.
Where does the label come from then?
When we create our XPage fields by dragging and dropping from the data source, the XPage created the following HTML automagically
The full demo includes more specific examples of labelifying specific fields and or sections and not others. To be able to reference specific panels the demo also includes x$ (link) which is provided as a JavaScript library in the sample database.
// --- 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.
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
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()
})
}