Archive | February 2014

Dojo in XPages – 8: Processing Each Element in a NodeList with forEach()

In the last post, we looked at using dojo.query() to select elements on the page. In this post, I’ll show how to process each element in the NodeList returned from dojo.query().

Dojo in XPages Series

Dojo in XPages — All Blog Posts

NodeList Methods

There are a number of methods available to process dojo NodeLists, but most of them are very similar to JavaScript array processing methods, so I won’t go into detail here.

Take a look at the dojo documentation for more information.

dojo.forEach()

The method I want to focus on is dojo.forEach(), because it can be used to process a NodeList that you select with using dojo.query()

Since dojo.query() returns a NodeList, you can just tack on the forEach() method to process it in the same statement. This is the syntax:

dojo.query('someQuery').forEach(function(node, index) { 
 logic here...
});

The forEach() method takes an anonymous callback function to process each node. That function will automatically receive parameters for the individual node and index number of that node being processed. The parameter names don’t matter and you don’t have to specify them at all if you don’t need to use them (although you generally want to at least have a handle to the node).

Here’s an example of getting a handle to all fields on the page and printing their values (and index number) to the console:

dojo.query('input').forEach(function(node, index) {
  console.log(index + ': ' + node.value);
});

Dojo 8a

Here’s an example of automatically setting the value of each field on the page:

dojo.query('input').forEach(function(node, index) {
  node.value = 'This is field ' + index;
});

Here is the result:

Dojo 8b

Up Next

Now that we know how to locate the elements we want to target and process the NodeList, we’ve got a great foundation to build upon. In the next few posts, we’ll look at more ways to manipulate elements on the page.

Dojo in XPages – 7: Selecting DOM Elements with dojo.query

In the last post, I showed how to easily select an individual element on the page by its ID with dojo. In this post, we’ll look at dojo.query(), a far more powerful feature that lets you select and manipulate multiple elements on the page.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

dojo.query()

dojo.query() is automatically available in XPages — it does not require loading an additional module.

It selects any number of elements on the page that match the CSS selector that you provide. You can choose elements based on the tag, class, id, attributes, or any combination thereof.

It can be used to access multiple elements and manipulate them simultaneously. When you run a query, it returns a NodeList.

What is a NodeList?

A NodeList which is an array of elements with additional functions available to make it more convenient to process (more on those in the next post). Even if the query only returns one element, an array is returned.

Selecting Elements

The same CSS selectors you use to target elements with stylesheet properties are used for selecting elements with dojo.query. You can select elements by tag name, class name, ID, use pseudoselectors, or combine them as needed for more specific targeting.

The selector is a string, so it can either be hard-coded or it can be replaced with a variable.

Standard Examples

Here are some of examples of how to use dojo.query to select elements:

Description Example
To select elements by tag name, specify the tag name dojo.query('li');
To select an element by ID, specify a pound sign (#) then the ID dojo.query('#myID');
To select elements by class name – specify a dot (.), then the class name dojo.query('.myClass');
To select elements of a specific tag with a specific class, specify the element name, a dot (.), then the class name dojo.query('li.myClass');
Select all img tags within any div that has the myDiv class dojo.query('div.myDiv img');
Select all elements that have a specific attribute dojo.query('[size]');
Select all elements with an attribute that has a specific value dojo.query('[size=5]');
Select odd or even elements with a pseudo class dojo.query('li:nth-child(odd)');

dojo.query('li:nth-child(even)');

Check out this W3Schools page for more information on CSS selectors. The better you understand them, the more efficiently you can target the elements you want to select on your page.

Test with Browser Tools!

As I mentioned in the last post, it can dramatically streamline your development when you work on code snippets directly in the browser console, rather than changing event code in XPages, saving, building, and refreshing the page.

This is especially true with working out query selectors. When you test a dojo query in the console, you’ll see an array of elements returned.

Dojo 7 - BrowserTools

To get a quick count of the number of elements a query will return, you can add .length to the end of the statement to get the array length.

Selecting XPages Components – A Caveat

The query selector cannot handle the colons that are part of every XPages component ID, so if you want to use dojo.query() to select a component by its ID, you’ll need to use the string replace() method in JavaScript to escape the colons so the query can be executed properly.

Does not work:
dojo.query('#view:_id1:inputText1');

dojo.query('##{id:inputText1}');

Works:
dojo.query('#view\\:_id1\\:inputText1');

dojo.query('##{id:inputText1}'.replace(/:/g, '\\:'));

The last example looks a little strange because of the double pound sign (#); the first pound sign specifies that you’re selecting an ID, then #{id:inputText1} is the EL syntax that gets replaced with the XPages component’s client-side JavaScriptID. The replace code uses a regular expression to replace all colons in the ID with \\:

There are two backslashes because the backslash is an escape character; the first backslash escapes the second one, so the resulting string has one backslash.

With all of that being said, the code is kind of confusing. It is often simpler to add an attribute or class name to XPages elements, so the dojo query can be easier to understand and maintain.

Up Next

dojo.query() is the starting point for a lot of dojo functionality. Now that we can select elements on the page, we’ll start looking at how to manipulate them in future posts.

Dojo in XPages – 6: Selecting a DOM Element with dojo.byId()

dojo.byId() is a simple function to get a handle to a DOM element on your page, based on it’s ID. Once you have that handle, you can manipulate it as needed. In this post, we’ll take a look at why and how to use it.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Why use it?

dojo.byId() is basically the same thing as document.getElementById(). So why is it worth using?

1. It’s shorter, so it’s convenient.
2. Older browser support. IE 7 and earlier will also return an element whose name attribute matches the value, which may not be the behavior you’re looking for.

Getting an HTML Field

Use of dojo.byId is very straightforward when obtaining a handle to a pure HTML element.

For example, if you have this field on your page:

<input id="htmlField" />

You can obtain a handle to it as follows:

var myField = dojo.byId('htmlField');

If you want to set the value of the field, you can get a handle to it, then set its value property.

dojo.byId('htmlField').value = 'new value 1';

One of the great things about working with client-side JavaScript is that you can also easily test this in the console of the browser tools without having to write it in Domino Designer, save, build, refresh, then test. Streamline your development by testing client-side code in the browser directly!

Dojo 6b

Getting an xp:inputText Field

As you are undoubtedly aware, client-side DOM element IDs of XPages components aren’t quite so simple. For example, on this simple page where I only have one xp component named xpField, the generated client-side id is view:_id1:xpField.

Fortunately, expression language (EL) syntax is available to compute that ID for you: "#{id:ComponentName}"

This code will get a handle to the field:

var myField = dojo.byId('#{id:xpField}');

When you include EL within client-side code, it is evaluated on the server and replaced with the actual string before being rendered on the page. (This works within both xp buttons and pass-thru HTML buttons.)

When you put that code on button, this is what gets rendered on the page:

var myField = dojo.byId('view:_id1:xpField');

To set the value of the field, you can use this code:

dojo.byId('#{id:xpField}').value = 'new value 2';

You can’t test this quite as easily in the browser console, but if you inspect the design and get the XPages ID of the field, you can still do it.

Dojo 6c

Once you’ve determined the client-side ID, you can write the code in the console to test it out.

Dojo 6d

We’ll dig into more DOM element manipulation later in the series, but for now, get used to using this handy method. Just watch the casing — you may be tempted to use dojo.byID(), but that won’t work because the last ‘D’ is capitalized.

Dojo in XPages – 5: Array Utilities

In this post, we’ll take a look at array manipulation functions that are available with dojo. The array module is not loaded automatically, but you can easily include it in order to make it available. In this post, I included it at the page level.

Dojo - 5

Now that the module is available on the page, we can take advantage of its methods.

  • indexOf()
  • lastIndexOf()
  • forEach()
  • some()
  • every()
  • filter()
  • map()

As with the string module functions, none of these are complicated procedures, but they can conveniently save you some time from having to write them yourself.

In fact, some of these methods (indexOf and lastIndexOf) are already available in modern browsers without using Dojo. The advantage of using the Dojo methods is cross-browser compatibility. The syntax of the built-in JavaScript method is more compact, but if you aren’t sure whether it will be supported, it’s safer to use Dojo. For example, I’ve been bitten by the use of the JavaScript’s Array.indexOf() because it fails in IE8 and below (or IE9 in compatibility mode).

Sample Array

This is the array that I will use for testing in all of the scripts. I don’t want to clutter this post by repeating it constantly, so I’ll just display it once here:

var myArray = ["car", "train", "boat", "plane", "car", "submarine", "helicopter", "motorcycle"];

indexOf()

Just like the string version of the method, indexOf returns the index of a specified element in the array. If that element is not found, it will return -1.

The method must accept two parameters, the array to search and the value to locate. In addition, there are two optional parameters — a starting index for where to begin searching the array and a boolean for whether it should locate the last instance of the specified value.

Code Result
dojo._base.array.indexOf(myArray, “car”); 0
dojo._base.array.indexOf(myArray, “spaceship”); -1
dojo._base.array.indexOf(myArray, “car”, 0, true); 0
dojo._base.array.indexOf(myArray, “car”, 3, true); 0
dojo._base.array.indexOf(myArray, “car”, 4, true); 4
dojo._base.array.indexOf(myArray, “car”, 10, true); 4

The first test returns 0, because the element ‘car’ is in the first position of the array and arrays are 0-based. The second test returns -1, because that value is not found in the array.

The next few tests returned unexpected values. Setting the 4th parameter to true tells it to return the last index of the specified value in the array. However, it doesn’t seem to work properly, based on the 3rd parameter, which is the starting index. The last index of ‘car’ in the array is 4, so the last four tests should have all returned 4. But it doesn’t work when the starting index is between 0 and 3.

My guess is that it’s a bug with the logic and that the ‘starting index’ parameter is being treated like a string character number rather than an array index number, which is quite confusing. In my opinion, this makes it dangerous to use. Fortunately, there’s a lastIndexOf method to solve that problem anyway.

lastIndexOf()

As you would expect, this method returns the position of the last instance of a specified value in an array.

The lastIndexOf method accepts the array to search and the value to find. It also accepts an optional third parameter for the starting point to search the array.

Code Result
dojo._base.array.lastIndexOf(myArray, “car”) 4

Pretty straightforward. In our array, the value ‘car’ is included twice and this returns the index of the last instance of it.

forEach()

This method iterates over an array and acts upon each element. It accepts the array and a callback function to process each element. You can optionally pass a third parameter with an object to scope the procedure.

Code Result
dojo._base.array.forEach(myArray, function(element) {
console.log(element += ‘X’);
});
carX
trainX
boatX
planeX
carX
submarineX
helicopterX
motorcycleX

some()

The some method returns a boolean value for whether at least one element in the array matches the provided condition. It will stop processing and return true as soon as it finds one match. Otherwise, it will return false.

The first example below checks whether at least one element in the array starts with the letter ‘c’. The second example checks whether at least one element in the array starts with the letter ‘x’.

Code Result
var hasMatch = dojo._base.array.some(myArray, function(element) {
return (element.substr(0,1) == ‘c’);
});
console.log(hasMatch);
true
var hasMatch = dojo._base.array.some(myArray, function(element) {
return (element.substr(0,1) == ‘x’);
});
console.log(hasMatch);
false

every()

The every method returns a boolean value for whether every element int he array matches the provided condition. It will stop processing and return false as soon as it finds one element that does not meet the condition.

The some and every methods can be more efficient than using the forEach method and checking every element in the array if you’re looking for 1 match or all matches, because they’ll short-circuit when the condition fails.

Code Result
var allMatch = dojo._base.array.every(myArray, function(element) {
return (element.length > 1);
});
console.log(allMatch);
true
var allMatch = dojo._base.array.every(myArray, function(element) {
return (element.length > 3);
});
false

filter()

The filter method filters an array and returns an array of values from the first array that return true when checked against the provided condition.

The first parameter is the original array and the second parameter is a function to process each array element and return true or false for whether it matches the filter.

This example checks the array and returns an array of values that start with the letter ‘c’.

Code Result
var filteredArray = dojo._base.array.filter(myArray, function(element) {
return (element.substr(0,1) == ‘c’);
});
console.log(filteredArray);
[“car”, “car”]

map()

The map method checks each element in the array, processes them according to the callback function, and returns

another array with the updated values. It does not modify the original array.

Code Result
var mappedArray = dojo._base.array.map(myArray, function(element) {
return (element + ‘Y’);
});
console.log(mappedArray);
[“carY”, “trainY”, “boatY”,
“planeY”, “carY”,
“submarineY”, “helicopterY”,
“motorcycleY”]

You can also use map with objects, although the original object is modified.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Dojo in XPages – 4: Performance Considerations when Choosing a Module Loading Style

In the last post, I demonstrated different ways to load dojo modules in order to make them available to use on your XPage or Custom Control. A comment on that post from Richard Moy got me thinking about the performance implications of each method, so I did some testing. In this post, I’ll show the effect of AMD-style loading versus including the module as a page resource.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

For the purpose of testing the difference, I created an XPage with a single button that uses dojo.fx.slideTo() to move the button because I know that dojo.fx is not automatically included on the page. In order to see the difference, I created one version of the page that loads dojo.fx as a page-level resource and another version of the page that loads uses AMD-style loading directly within the button code. I then loaded the pages and monitored the network activity with Chrome’s dev tools.

@Iq.js

@Iq.js is a file that contains aggregated JavaScript for an XPage. Since some dojo modules are automatically included on the page, it specifies those modules.

On the page that loads dojo.fx with AMD, this file was 17.4kb.

On the page that loads dojo.fx as a page-level resource, this file was 19.3kb. When looking at the file in the dev tools, I can see that it includes dojo.fx. Because I specified it as a page-level resource, it included it with the other JavaScript loaded on the page.

Here’s a snippet of @Iq.js on the page that includes dojo.fx as a page resource (with most of the code trimmed out).

/* XPages Aggregated JavaScript */
require({cache:{
'dojo/fx/Toggler':function(){
//>>built
define("dojo/fx/Toggler",...

'dojo/fx':function(){
//>>built
define("dojo/fx",...

'dojo/i18n':function(){
//>>built
define("dojo/i18n",...

'dojo/string':function(){
//>>built
define("dojo/string",...

...

AMD Loading

In my testing, AMD loading proved to be true to it’s name, loading the dojo.fx module asynchronously (as opposed to during the initial page load, mixed in with @Iq.js).

Dojo_4_FX AMD 1

It was not included on the page until the moment that the button was clicked, at which point, two more GET requests were made and the additional files were loaded.

Dojo_4_FX AMD 2

My Observations

After the module is loaded asynchronously, it appeared to be cached for the remainder of the browser session (although I did not test this extensively).

The AMD style of loading has the benefit of trimming the initial load time, but it then has to perform the load at the time an action is clicked. Theoretically, this will slow down the action to some extent, but that difference is imperceptible when testing the asynchronous load of a single small module on a local development server.

I don’t think there’s one always-right approach; it depends on the needs of your application. If initial load time is the top priority, then AMD loading will help in that respect. If page load time is plenty fast and you’re more concerned either responsiveness at the time an action is clicked or about maintenance of the code being a little simpler (especially when using the same modules in several places on the same page), then it seems to me that the code can be a little cleaner by loading the required module(s) at the page (or theme) level.

Dojo in XPages – 3: Loading Dojo Modules

In the last post, I showed how to use some string utilities that are automatically loaded and available to any XPage. However, there are more Dojo modules with great functionality that are on the server but not loaded by default so as not to introduce unnecessary overhead when loading your pages. In this post, I’ll show how to load additional modules when you want to use their features so you can easily include any Dojo module that’s already on the server.

In this post I’ll use an example of an array utility called indexOf, which works on arrays like the standard JavaScript indexOf function works on strings. We’ll get to array utilities more in the next post, but we need to first pause to look at how we can load the array module so that we can use it, because it’s not automatically loaded in the XPages context.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Option 1 – Loading On Demand

You can load a Dojo module on demand and use it within a function as shown below.

require(["dojo/_base/array"], function(array) {
  var myArray = ["car", "train", "boat", "plane", "car", "submarine", "helicopter", "motorcycle"];
  console.log(array.indexOf(myArray, "car"));
});

This loads the array module and makes it available within the function based on the object named array. If you try to reference array outside of the function, it will be out of scope and will raise an error.

Note that this is a newer style of loading modules as of Dojo 1.7, so it will work in Notes 9 (which uses dojo 1.8), but not in Notes 8.x (which uses 1.6 and below). See the first post in the series for more details.

Option 2 – Loading at the Page Level

You can also load a Dojo module at the page level in order to make it available to any component on the page, rather than just within the scope of a single function.

XPages and Custom Controls give you properties to make this easy.

On the Resources subtab of the page properties, click the Add... button and select Dojo Module. Then type in the name of the module that you want to load and click OK.

Dojo_3_LoadDojoModule

This includes an xp:dojoModule tag in the page source.

<xp:this.resources>
  <xp:dojoModule name="dojo._base.array"></xp:dojoModule>
</xp:this.resources>

This makes the module available for use throughout the page, but note that you have to refer to it as dojo._base.array.indexOf(). This is different than with the first option, which assigned a variable to refer to the module within the scope of the function.

Here’s an example of how to use it:

dojo._base.array.indexOf(myArray, "car")

Option 3 – Loading Application-Wide via a Theme

If you’re going to use the function throughout your application, then you can make it available throughout the application by including it in a theme.

<resources>
  <dojoModule name="dojo._base.array"></dojoModule>
</resources>

With this method, you would use it the same way as shown in option 2, because it’s essentially being loaded the same way.

Dojo in XPages – 2: String Manipulation

In this post, we’ll take a look at dojo string manipulation utilities. The String utilities are included automatically in XPages — you don’t have to do anything extra in order to use them. You just have to refer to them with the proper hierarchy. Since they’re in the string library, the methods will be called in this form: dojo.string.method()

Dojo in XPages Series

Dojo in XPages — All Blog Posts

trim

The trim function trims whitespace before and after a string. It does not trim redundant space within the string.

Code Result
dojo.string.trim("       trim     this   string      ") trim     this   string

This may seem pointless because there’s a string.trim() method in JavaScript, but it’s not supported in IE8 and below, so the Dojo version works around that and give you a safer cross-browser option, which is one of the main points of using a client-side JavaScript library.

pad

The pad function gives you the ability to easily ensure that a string is at least a certain size. The first parameter is the string to pad. The second parameter is the minimum number of characters. The third (optional) parameter is the character to use to pad the string. The default is 0. The fourth (optional) parameter, when set to true, will pad the string on the right; by default it will pad to the left.

Here are some examples:

Code Result
dojo.string.pad("pad 20", 20) 00000000000000pad 20
dojo.string.pad("pad 20", 20, ' ')               pad 20
dojo.string.pad("pad 20", 20, '^', true) pad 20^^^^^^^^^^^^^^
dojo.string.pad("pad 20", 20, 'ABCDEF', true) pad 20ABCDEFABCDEFABCDEF
  • Example 1 uses the defaults of padding to the left and with a 0.
  • Example 2 pads with a blank space.
  • Example 3 pads to the right with a caret (^)
  • Example 4 is interesting. You can define more than one character to pad the string. When you do that, it will use your pattern and apply it as many times as needed in order to reach the minimum length. It will not stop with a partial instance of the pattern, though, so, as you can see in the last example, it will go longer than your minimum if it needs more space to finish the pattern.

rep

The rep function repeats the specified string the specified number of times.

Code Result
dojo.string.rep("RepeatThis", 5) RepeatThisRepeatThisRepeatThisRepeatThisRepeatThis

substitute

The substitute function uses a templated pattern to replace values in the string. The first parameter is a string with placeholders that you intend to replace, in this form ${placeholderName}. The second parameter defines the values to replace in each placeholder, based on matching the name.

Code Result
dojo.string.substitute("Hello ${last}, ${first}", {last: "John", first: "Smith"}) Hello ,
var dollar = '$';
dojo.string.substitute("Hello " + dollar + "{last}, " + dollar + "{first}", {last: "John", first: "Smith"}));
Hello John, Smith

The first example shows how it’s supposed to work. However, the server doesn’t like the dollar signs very much, so it removes them, because it looks to replace code in this format in client-side JavaScript with something computed from the server: ${ }.

The second example works around this limitation and lets the function work within the XPages context. (I also tried to escape the dollar sign, but it threw errors and wouldn’t even load the page.)

All in all, these functions are nothing fancy, but, they can be handy and they’ll save you some time if you need these features.

SSJS Caveat

Keep in mind that Dojo is a client-side JavaScript library, so this code will not work in server-side JavaScript. If you try to refer to Dojo in SSJS, you’ll get a ‘dojo’ not found message.

Dojo2_SSJSError