Quantcast
Channel: XPages – Xcellerant
Viewing all 216 articles
Browse latest View live

XSPUserAgent Methods Do Not Detect IE 11

$
0
0

I’ve found that the methods of the XSPUserAgent class, which are commonly used for browser detection in XPages applications, do not properly detect IE 11. In this post, I’ll show what happens and how to work around it.

Browser Detection Methods

It is standard practice to use methods like these on rendered formulas for resources (in a theme or directly on a page) that you want to conditionally load, based on the browser type:

<xp:styleSheet rendered="#{javascript:context.getUserAgent().isIE()}" href="/ie_stylesheet.css"/>
<xp:styleSheet rendered="#{javascript:context.getUserAgent().isFirefox()}" href="/firefox_stylesheet.css"/>
<xp:styleSheet rendered="#{javascript:context.getUserAgent().isChrome()}" href="/chrome_stylesheet.css"/>

Unfortunately, the isIE() check fails for IE 11.

XSPUserAgent Method Values for IE 11

Method Value Returned
context.getUserAgent().isIE() false
context.getUserAgent().getBrowser() ‘Unknown’
context.getUserAgent().getBrowserVersion() (blank)
context.getUserAgent().getBrowserVersionNumber() 0

I tested and found these return values to be the case on both 8.5.2 (which doesn’t surprise me) and Notes 9 (which does).

Checking the Full Browser String

Fortunately, this method will return the full browser string:

context.getUserAgent().getUserAgent()

In IE 11, the string is as follows:

Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko

Apparently, Microsoft made some changes to their standard pattern for the browser string (including removing the easily-detectable ‘MSIE’).

Fortunately, two things stand out about this string:

  1. Trident/7.0 >> IE10 included Trident/6.0 and IE9 included Trident/5.0
  2. rv:11.0 >> Indicates version 11 of Internet Explorer

I don’t feel like it’s a future-proof solution, but for now, I’m checking the browser string for those values. Just in case point release numbers change, I’m checking for ‘Trident/7′ and ‘rv:11′.

Here’s a simple function that you can use to check for IE 11:

function isIE11() {
  var isBrowserIE11 = false;
	
  if (context.getUserAgent().getBrowser() == 'Unknown') {
    var userAgent = context.getUserAgent().getUserAgent();
    if (userAgent.indexOf('Trident/7') > -1 && userAgent.indexOf('rv:11') > -1) {
      isBrowserIE11 = true;
    }
  }	
 
  return isBrowserIE11;
}


Dojo Data Grid – Part 33: Reading Data from a Custom REST Service

$
0
0

Setting the data source of a grid is generally very straightforward when using one of the provided view or Json rest services, but it doesn’t work the same way for a custom REST service. In this post, I’ll show how to get it to work.

Dojo Data Grid Series

Dojo Grids in XPages — All Blog Posts

Error Binding to a Custom REST Service

Normally, you add a REST service to the page to provide data and select it in the storeComponentId of the grid (as shown in ), but it throws an error when you do this with a custom REST service. (I tried binding the REST service to the grid based on both the id and jsId properties of the custom REST service, but the result was the same.)

The grid would not display on the page and Firebug would show this error:

ReferenceError: restService1 is not defined

Solution

In order to find a solution, I did some digging into what a pure dojo grid needs (outside of XPages) for a JSON data store.

Key points in the solution:

  • Include two dojo modules in the page resources to set up the data store
  • A pass-thru script tag with code to set up a JSON data store for the grid (uses the dojo modules that the resources specify)
  • The grid’s store property is set to the variable set up for the data source in the tag. (storeComponentId needs an XPages component name.)

Below is the entire source of an XPage that demonstrates this technique.

Lines 05-08 include two dojo modules that you need in order to set up the data source.

Lines 10-46 define a custom REST service. The pathInfo value in line 10 will be used to read the data. The majority of this is hard-coded data, which you would replace with your code to provide data for the grid.

Lines 48-53 are a standard client-side script tag that set up the data store and make it available for the grid. The jsonStore object reads the REST service data via URL path, so it needs the XPage name and the pathInfo value for the REST service. Modify this line as needed to point to the page and REST service.

Lines 55-58 define the grid, but the key is that line 55 sets the store property to the name of the JavaScript object defined in line 52.

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

  <xp:this.resources>
    <xp:dojoModule name="dojo.store.JsonRest"></xp:dojoModule>
    <xp:dojoModule name="dojo.data.ObjectStore"></xp:dojoModule>
  </xp:this.resources>
	
  <xe:restService id="restService1" pathInfo="gridData">
    <xe:this.service>
      <xe:customRestService contentType="application/json"
        requestVar="customData2" requestContentType="application/json">
      <xe:this.doGet><![CDATA[#{javascript:// Create hard-coded test data
var jsonData = [];

var thisEntry = {};
thisEntry.Status = 'Open';
thisEntry.Name = 'John';
jsonData.push(thisEntry);

thisEntry = {};
thisEntry.Status = 'Closed';
thisEntry.Name = 'Bill';
jsonData.push(thisEntry);

thisEntry = {};
thisEntry.Status = 'Closed';
thisEntry.Name = 'Mike';
jsonData.push(thisEntry);

thisEntry = {};
thisEntry.Status = 'Open';
thisEntry.Name = 'Jim';
jsonData.push(thisEntry);

thisEntry = {};
thisEntry.Status = 'Open';
thisEntry.Name = 'Steve';
jsonData.push(thisEntry);

return toJson(jsonData);}]]>
        </xe:this.doGet>
      </xe:customRestService>
    </xe:this.service>
  </xe:restService>
	
  <script>
    var jsonStore = new dojo.store.JsonRest(
      {target:"CURRENT_PAGE_NAME_HERE.xsp/gridData"}
    );	
    var dataStore = dojo.data.ObjectStore({objectStore: jsonStore});
  </script>
	
  <xe:djxDataGrid id="djxDataGrid1" store="dataStore">
    <xe:djxDataGridColumn id="djxDataGridColumn2" field="Name"></xe:djxDataGridColumn>
    <xe:djxDataGridColumn id="djxDataGridColumn1" field="Status"></xe:djxDataGridColumn>
  </xe:djxDataGrid>
</xp:view>


Conditionally Render a Passthru tag within the body of an XPage.

$
0
0

I recently had a need to conditionally include a new client-side javascript library on an XPage after another library was loaded. The other library was loaded within the body of the page and not within the header, so, in this case, it did not work to include the new library as a page resource.

However, when I put an <xp:script> tag within the body of the page, I received this error:

The complex type tag (xp:script) must be within a property tag, like <this.something>

Normally, those tags appear within <this.resources>.

So, I needed a way to conditionally include a passthru <script> tag to load the library.

My solution was to wrap it within an <xp:span> tag. Since it’s an xp tag, I can use a server-side rendered formula. If it returns true, then the passthru script tag will be added to the page. Admittedly, it feels like a bit of a hack, but it does the job:

<xp:span rendered="#{javascript:context.getUserAgent().isIE()}" >
	<script src="MyLibrary.js" />
</xp:span>

For the record…

I’m not advocating this as ideal design. But if there is some case where you need to conditionally include a script tag within the bod of an XPage, this will do the trick. In my case, I was updating an application and needed to keep the changes to a minimum.

Library Relative Reference

There’s a secondary tip in here, which Marky Roden pointed out. With a passthru <script> tag, do not start the src url with a backslash (/), even though that’s what the <xp:script> tags do. On a passthru <script> tag, that will be relative to the server root.


Dojo Data Grid – Part 34: Customizing Cell Styles Based on Data

$
0
0

In a previous post, I showed how to set the row color based on data in the row. In this post, I’ll show how style a cell individually based on the data that it contains.

Dojo Data Grid Series

Dojo Grids in XPages — All Blog Posts

Formatter Function

The key to this solution is a formatter function, which is a client-side JavaScript function that can be used to process a grid cell. (See this post for another example of how to use it.)

All you have to do is specify the name of the function on the grid column’s formatter property and and it will run on every cell as it is rendered. You can use a formatter function to modify the cell data or change the styling. The cell will display the value that is returned, to return the original value if you do not modify it.

Grid34_B

Below are examples formatter functions that use inline styles and CSS classes. Both use different approaches to change the font color to red if the name starts with ‘Ad’. The formatter function automatically receives a handle to the cell itself when its called, so you can use either the customStyles property or the customClasses property of the cell to modify the styles.

<script>
  function colorCell_InlineStyle(value, rowIndex, cell) {
    if (value.substr(0,2) == 'Ad') {
      cell.customStyles.push('color:red');
    }	
    return value; 
  }
		
  function colorCell_Class(value, rowIndex, cell) {
    if (value.substr(0,2) == 'Ad') {
      cell.customClasses.push('redCell');
    }
    return value; 
  }
</script>

Grid34_A


Creating an Application Layout with CSS/HTML Provided by a Designer

$
0
0

I recently started working on an XPages application for which the UI had already been designed by a visual designer. In this post, I’ll cover the steps I took to implement the provided design within the application.

I must admit, it was a pleasant surprise to discover that sample pages had already been created to define the UI of the application; the visual designer sent me a list of HTML files, images, and style sheets. In my experience, this has not been the norm, so I took a few minutes to think through how to make the best use of those files as efficiently as possible.

I was involved in a project awhile back where a UI was mocked up and CSS was provided, but it was left to the developers to get the XPages application looking just right. It can be a challenge to try to make sense of the massive style sheets that are generated by some UI design tools (which seem to add many levels of div tags to get everything just so). Fortunately, in this case, I was given not only CSS but also sample pages.

In short, my goal was (a) to get the static page and styles working in an XPages application, (b) to modularize the layout, and (c) make it dynamic one piece at a time.

Here is the approach that I took:

A. Implement Static HTML in XPages

    1. Import CSS and images into the application
    2. Account for everything included in the <head> section; create a Theme that includes the CSS in the application (and set the application to use the theme)
    3. Copy the HTML from a static page into a custom control called ccLayout
    4. Create an XPage that contains ccLayout
    5. Clean up the HTML in ccLayout as needed (adding closing tags where missing, etc)
    6. Test the page and fix CSS references as needed (many image references are looking for subfolders, but those directory paths can be modified once image resources are in the app)

At this point the goal is to have the XPage looking exactly like the sample page. Once you’ve achieved that, you’re ready to modularize it.

B. Modularize the Layout

    1. Break up the static html into logical chunks and move the chunks to separate custom controls for each component of the layout. Some examples:
      1. Title bar
      2. Top navigation
      3. Search bar
      4. Side navigation
      5. Footer
    2. Replace those chunks of HTML with references to the layout component controls in the correct areas within the main layout control
    3. Move the main content HTML out to a separate control
    4. Put an Editable Area control in place of the main content area.
      1. The main content will change from page to page, so the editable area lets you keep the same general layout, but put different content in the layout on each page, all while reusing a single layout custom control
      2. If multiple areas of the layout change from page to page, then use more editable areas

Now, you have an application layout control and separate controls for components reused throughout the layout. You can now reuse the layout easily throughout the application.

Ultimately, this UI is not based on OneUI or Bootstrap or any other framework. It’s a fully-customized design. Some of the concepts that Paul Della-Nebbia and I covered at our MWLUG session and TLCC webinar on creating custom layouts are foundational to this approach of creating a custom application layout.

Here are the slide show and webinar recording if you’d like to take a look.

C. Make the Application Dynamic

  1. Go back through the layout controls and build in live links, etc as needed
  2. Build the content custom controls (forms, views, etc)

Now, for each form or view that you need to display, all you have to do is create an XPage, add the ccLayout control, and then drop in the form or view custom control into the editable area and you’ll have a consistent UI throughout the application.

It doesn’t seem very complicated looking back at the list of steps, but it was a good exercise to go through.

Feedback

I’m curious — have you ever been provided a working static UI for a web application? If so, how did you go about using it?


Dojo Data Grid – Part 35: Suppressing Blank Rows Due to Readers Fields

$
0
0

Over the last few months, I’ve received several messages and read several posts trying to figure out how to suppress blank rows in a grid due to Readers field security on the documents in the underlying view. In this post, I’ll share an anticlimatcally-simple solution to this white whale of a problem.

Dojo Data Grid Series

Dojo Grids in XPages — All Blog Posts

The Problem

When you have a Dojo data grid that displays entries from a view via a REST service, you will see one row for each entry in the underlying view. If there are documents that are hidden from the user due to readers field security, you will still see a row for each of those documents, but they will be virtually blank (displaying … in each cell).

So, if you have 10,000 documents in a view, but the user can only see 100 of them, there will be 9,900 blank lines to scroll through in the view.

It’s a maddening issue. I’ve heard multiple people say that they stopped trying to use a dojo grid because of this.

Attempted Solutions

I spent hours trying to handle this issue in numerous ways.

I tried code that tried to check the row contents (similarly to this post) to apply a css class to hide it when blank, but I found that *every* row starts out with the default cell content (…) and then, on a second pass, fills in the real data. When I looked for an empty row and applied the class, it would hide every row before the valid ones were filled in.

I made several attempts at writing code that scanned the current set of records in memory (a block of rows the same size as the rowsPerPage property), but I found that the block of rows in memory was constantly shifting, so I couldn’t check the current block and suppress rows as needed dynamically.

I also noticed that the grid’s row.index property and the @Position attribute of a grid entry got out of synch as soon as there was a document hidden due to reader security. However, it didn’t display the blank rows inline — it moved them all to the end. This was also very problematic in checking data and determining what to suppress.

Ultimately, I realized that I can’t help the fact that the built-in REST service appears to look at view meta data and tell the REST service that it’s returning the number of elements corresponding to the total number of documents in the underlying view, regardless of the security.

A Solution

Then it occurred to me that I wouldn’t have to try to scan through the rows in memory if the entire set was in memory. Then it would just be a matter of counting how many actual rows were generated and hiding the rest.

I noticed that the REST service doesn’t actually include blank rows, but it does include a row count at the beginning that tells the grid how many rows to render. The grid will include all valid rows and then fill in the rest with blank rows.

To solve the problem, you can load all rows into memory and the use the onStyleRow event handler (see this post for more info) to hide the blank rows (which come after the actual row count has been reached).

Follow these two steps to implement the solution:

1) Set the rowsPerPage property of the grid to a number that is equal to (or greater than) the number of rows that could be included in the grid

2) Put this code in the onStyleRow property of the grid:

var row = arguments[0];

if (row.index >= restViewItemFileService['_items'].length) {
 row.customStyles += 'display:none;';
}

This assumes that you’re using a restViewItemFileService type of REST service. The second line would be different for a viewJsonService.

In the code above, ‘restViewItemFileService’ is the name of the REST service. Change it to match the ID of your rest service.

All in all, I spent hours and probably wrote a few hundred lines of code in various attempts in order to come up with what was ultimately a property change and a 4-line solution!

Caveat

Performance is certainly a big factor in whether this solution will work in your grid, because all documents must be loaded into memory rather than pre-loading a small chunk and then loading the rest on demand.

Another Potential Approach

Another approach that may work (but one that I have not yet tried) would be creating a custom REST service that only returns rows based on the current user’s security. That would seem to be a valid approach, but this post was focused on solving the problem with the provided view REST service types.


Changing the Output Tag on a Computed Field in XPages

$
0
0

By default, a computed field writes out a <span> tag to the page around the contents of the field, but you can use the tagName property to change that.

For example, this computed field…

<xp:text escape="true" id="computedField1" 
  value="#{javascript:return 'Hello, World';}">
</xp:text>

… will result in this html:

<span id="view:_id1:computedField1" class="xspTextComputedField">
  Hello, World
</span>

But, if you change the tagName property to something else — for example a list item (li)…

<xp:text escape="true" id="computedField1"
  value="#{javascript:return 'Hello, World';}" 
  tagName="li">
</xp:text>

… it will produce this html:

<li id="view:_id1:computedField1" class="xspTextComputedField">
  Hello, World
</li>

The class name stays the same, but the surrounding tag changes. This can come in very handy.


Dojo Data Grid – Part 36: Conditionally Preventing Cell Editing

$
0
0

With the editable property of a grid column, it’s easy to enable editing or even conditionally compute whether the entire column should be editable. In this post, I’ll show how to take finer control and dynamically determine whether to enable editing based on the contents within a cell.

Dojo Data Grid Series

Dojo Grids in XPages — All Blog Posts

canEdit Event

In short, you can use attach a function to the canEdit event of the grid to return true or false, based on whether you want the cell to be editable.

Interestingly, this function overrides the default settings, so you can allow any cell to be editable or not editable at any time, regardless of the grid column’s editable property.

An Example

Below is an example of code that allows the LastName column of the grid to be editable, but only if the last name in the current cell starts with ‘A’. The code should be run on the onClientLoad event of the page.

The function automatically receives two parameters that are a handle to the grid column and the row index of the clicked cell.

The event will fire when the user double-clicks on a cell (or single-clicks, based on a grid property) in an attempt to edit it, so lines 3-4 streamline the amount of work to be done by not checking the data of columns that aren’t set to be editable anyway.

Lines 8-9 also streamline the logic by only checking data in the lastname column.

Lines 13-15 retrieve the field value from the REST service, based on the column name and the row index.

Lines 18-19 checks whether the value in the cell starts with ‘A’ and will return true if that is the case. Otherwise, it will return false and the cell will not be editable.

dijit.byId('#{id:djxDataGrid1}').canEdit = function(cellColumn, rowIndex){
	
  if (!cellColumn.editable) {
    return false;
  } else {
  
    // Only check the lastname column
    if (cellColumn.field != 'lastname') {
      return false;
      
    } else {
      // Get the clicked cell value from the grid -- doesn't seem accessible with a property of the object passed in
      var grid = cellColumn.grid;
      var item = grid.getItem(rowIndex);
      var fieldValue = item[cellColumn.field];
	  
      // Determine the condition for which it's not editable and return false
      if (fieldValue.substr(0,1) == 'A') {
        return true;
      } else {
        return false;
      }
    }
  }
      
}

Note: this is designed to work with a viewJsonService type of rest service — the viewItemFileService would require slightly different logic when retrieving the current cell value.



Dojo in XPages – 1: Overview

$
0
0

Dojo is a client-side JavaScript library that is included on the Domino server and is automatically available within XPages application. As with any library, it contains many features that save you time by providing many features so that you don’t have to take the time to write them yourself. In this series, I’ll cover basic Dojo functionality and explore how you can use it to increase your productivity and your applications’ functionality.

The Case for Libraries

Several client-side JavaScript libraries are available. They provide features that you can easily use in order to speed up your development time. For example, you may need some string or array manipulation logic that doesn’t exist in core JavaScript, but a library might provide it so you don’t have to take time writing it. Libraries also make it easy for you to add/remove elements on a page, change styles, move elements, provide animation, draw simple charts, and more. This lets you focus your time on your business logic rather than on the supporting functionality.

Even more importantly, libraries generally provide cross-browser support with the provided features so you don’t have to worry about it. If you’ve ever spent time writing code that has to support several versions of IE along with other browsers, you understand how much time and hassle this will save you.

Dojo Packages

The Dojo toolkit is comprised of 4 main packages. Here is a list and description, taken directly from the Dojo documentation:

  • dojo – Sometimes referred to as the “core”, this is the main part of Dojo and the most generally applicable packages and modules are contained in here. The core covers a wide range of functionality like AJAX, DOM manipulation, class-type programming, events, promises, data stores, drag-and-drop and internationalization libraries.
  • dijit – An extensive set of widgets (user interface components) and the underlying system to support them. It is built fully on-top of the Dojo core.
  • dojox – A collection of packages and modules that provide a vast array of functionality that are built upon both the Dojo core and Dijit. Packages and modules contained in DojoX will have varying degrees of maturity, denoted within the README files of each package. Some of the modules are extremely mature and some are highly experimental.
  • util – Various tools that support the rest of the toolkit, like being able to build, test and document code.

The core functions are automatically available to XPages applications. Many XPages components are built on dijits, but if you want to use dijit or dojox modules, you’ll need to include them on your page. I do not believe that the util package is included with Domino.

Dojo Version

There’s decent help available online, but you need to pay attention to the Dojo version number built into your current version of Domino in order to find the correct reference.

  • Domino 8.5.1 – Dojo 1.3.2
  • Domino 8.5.2 – Dojo 1.4.1
  • Domino 8.5.3 – Dojo 1.6.1
  • Domino 9.0 – Dojo 1.8.1
  • Domino 9.0.1 – Dojo 1.8.3

You can install a different version of dojo on the server and change it server-wide or on a per-application basis, but I would not recommend it unless you have a very compelling reason. A lot of XPages functionality depends on Dojo, so tread lightly.

Other JavaScript Libraries

Other libraries provide many of the same features. However, another library may provide more code or more plugins for additional features that aren’t available in the base library.

The most popular library is jQuery, and Mark Roden has written extensively on using it with XPages.

The downside is that it’s an additional library to load, so there’s some extra overhead.

If you need features and plugins that Dojo doesn’t provide, by all means, use another library. But if you can accomplish the task with a similar amount of effort, then be in the habit of using the functionality that’s already available.

There’s way more to Dojo than I could possibly cover, but I’ll highlight key features that I hope you’ll find most useful.


Article Published in The VIEW: Reusable and Consistent Message Displays in XPages Part 2: Dialogs

$
0
0

The second part of the series on reusable message displays in XPages has been published in The VIEW (subscription required).

The separation of client-side logic and server-side logic in XPages (read: web) applications increases the complexity of communicating consistently to users. In this series, we demonstrate several implementations that save development time by being easy to implement and that help improve the user experience by providing consistently formatted messages. In Part 1 of this series, we demonstrated a reusable control for inline messages. In Part 2, we provide options for displaying messages in OneUI- and Bootstrap-themed dialogs.


Dojo in XPages – 2: String Manipulation

$
0
0

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

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


Dojo in XPages – 3: Loading Dojo Modules

$
0
0

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.

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 – 4: Performance Considerations when Choosing a Module Loading Style

$
0
0

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.

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 – 5: Array Utilities

$
0
0

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 – 6: Selecting a DOM Element with dojo.byId()

$
0
0

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.

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 – 7: Selecting DOM Elements with dojo.query

$
0
0

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.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 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 – 8: Processing Each Element in a NodeList with forEach()

$
0
0

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

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 ' + i;
});

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 – 9: Manipulating DOM Element Styles and Classes

$
0
0

We recently saw how to select DOM elements with dojo.query(). In this post, I’ll show how you can change class names and styles of the selected elements.

Adding and Removing Classes

If you have classes already set up in style sheets, you can use dojo to easily add or remove class names from one or more elements. Use this syntax if you just need to modify a single element:

dojo.addClass('ID/Node', 'myClass');
dojo.removeClass('ID/Node', 'myClass');

The first parameter can either be an element’s ID or a handle to a dojo node. (For more information about XPages element IDs, see this post.)

Chaining Dojo Methods

If you want to modify multiple elements, you can select them with dojo.query() and easily modify them with one step. You can use the single-node processing statements shown above with dojo.forEach() or you can chain the addClass() or removeClass() methods to the original query to take care of the selection and modification in a single statement.

For example, if I want to select all of the LI tags on the page and either add or remove a class from all of them, I could use one of these statements:

dojo.query('li').addClass('myClass');
dojo.query('li').removeClass('myClass');

Changing Style Properties

Dojo has a style() method that allows you to change style properties.

To change the style of a single element, specify the element ID or a handle to a dojo node, the property name, and the value. For example, to hide a field with the ID input1, you can set it’s display property to none, as follows:

dojo.style('input1', 'display', 'none');

Property names are similar to CSS property names, but if the standard CSS property name has a hyphen, then the hyphen is removed and the property is written in camel case. For example, background-color changes to backgroundColor.

To change style properties on multiple elements, chain the style() method to a dojo.query(). These examples hide all odd LI elements and change the color of the event elements to red.

dojo.query('li:nth-child(odd)').style('display', 'none');
dojo.query('li:nth-child(even)').style('color', 'red');

Changing Multiple Style Properties

If you want to change multiple style properties with a single statement, you can use this syntax:

dojo.query('x').style({
  property:value,
  property:value,
  property:value
})

This example changes the color and font size and adds an underline. (Note that the text-decoration and font-size properties are modified to remove the hyphen and capitalize the second part.)

dojo.query('li').style({
  color:"#00FF00",
  textDecoration:"underline",
  fontSize:"24px"
})

With these functions, you can easily modify the styling of any element on your pages.


Dojo in XPages – 10: Creating and Moving DOM Elements with dojo.create and dojo.place

$
0
0

In the last post, we looked at changing the styles and classes of DOM elements dynamically with Dojo. In this post, I’ll show how you can move elements around on the page and insert new elements dynamically.

Both of these methods are available without including any additional dojo modules in XPages.

Moving DOM Elements with dojo.place()

The dojo.place() method allows you to move an element to a new location on the page. When you call it, you define the element that will be moved, the element that it will be moved to, and define the relative position.

dojo.place(element ID/node, target ID/Node, relativePosition)

There are 6 options for the relative position: before, after, replace, only, first, last

before and after are pretty self-explanatory. replace will remove the target element and replace it with the element that is being moved.

The other three options are intended to be used when the target element has child elements. only removes all of the target element’s child nodes and makes the element being moved the only child of the target node. (only has the same effect as replace when targeting an element with no children.) first and last put the element to move as the first or last child of the target node, respectively.

Let’s look at some very simplistic examples.

I have a very simple page that has 3 divs that look like colored squares.

<div id="redDiv" style="height:100px; width:100px; background-color:#FF0000;"></div>
<div id="greenDiv" style="height:100px; width:100px; background-color:#00FF00;"></div>
<div id="blueDiv" style="height:100px; width:100px; background-color:#0000FF;"></div>

Here’s what it looks like by default:

Dojo_10a

This statement takes the red div and moves it after the blue div:

dojo.place('redDiv', 'blueDiv', 'after');

Dojo_10b

This statement takes the red div and moves it before the blue div:

dojo.place('redDiv', 'blueDiv', 'before');

Dojo_10c

This statement removes the blue div by replacing it with the red div:

dojo.place('redDiv', 'blueDiv', 'replace');

Dojo_10d

Now, let’s look at the other 3 options while dealing with a list, which has child list items. Here, I have two lists with 3 items each:

<ul id="list1">
  <li id="a1">a</li>
  <li id="b1">b</li>
  <li id="c1">c</li>
</ul>

<ul id="list2">
  <li id="x2">x</li>
  <li id="y2">y</li>
  <li id="z2">z</li>
</ul>

Dojo_10e

This statement takes list element a1 and makes it the last element in list2:

dojo.place('a1', 'list2', 'last');

Dojo_10f

This statement takes list element b1 and makes it the first element in list2:

dojo.place('b1', 'list2', 'first');

Dojo_10g

This statement takes list element c1 and makes it the only list item in list2, by removing all of the items currently in list2:

dojo.place('c1', 'list2', 'only');

Dojo_10h

Here’s what the page source now looks like. list1 has no items and list2 only has item c1.

<ul id="list1"></ul>
<ul id="list2">
  <li id="c1">c</li>
</ul>

Creating an Element with dojo.place()

In addition to moving items, you can also use dojo.place() to create a simple DOM element and place it in a single step.

Starting with our original 3 colored div squares, this code will create a gray div square and place it after the green div.

dojo.place('<div style="height:100px; width:100px; background-color:#777777;"></div>', 'greenDiv', 'after');

Dojo_10i

Creating DOM Elements Dynamically with dojo.create()

If you need to add more complex DOM elements dynamically to your page, you can both create and place them with dojo.create().

Similarly to the last dojo.place() example, this code will create a gray div square (and add some text to it) and place it in a single statement.

This syntax, however, allows for cleaner syntax of DOM element creation if you have more than just a few properties to set.

dojo.create('div', 
  {
    style: 
      {
        backgroundColor:'#777777', 
        height:'100px', 
        width:'100px'
      },
  innerHTML: 'This div was created dynamically'
  },
  'greenDiv',
  'after'
)

As mentioned in the last post, property names come from CSS property names, but properties that are hyphenated in CSS drop the hyphen and become camel case in dojo methods, which is why the background-color property is written as backgroundColor.

Dojo_10j


Dojo in XPages 11 – Using Loading Images to Improve Perceived Responsiveness

$
0
0

In the last post, I showed how to create and move DOM elements dynamically. In this post, we’ll look at a great use case for that functionality — using loading images to improved the perceived responsiveness of the page.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Why use loading images

You’ve undoubtedly seen applications where you click (or swipe) to trigger some action or data load and you then see some type of animated image that indicates that something is loading or processing. This is a common convention to convey to the user that something is happening. If it takes 10 seconds to respond, but the user receives that indication that something is happening, it will appear to be much more responsive than clicking a button and waiting 10 seconds without any indication that the desired action was triggered.

If the user doesn’t receive feedback very quickly, they may click again (multiple times) or leave the page altogether and assume that something is broken.

But this perceived responsiveness can be easily improved with the use of an image that shows the user that their action was triggered and something is happening. This can go a long way toward improving users’ perception about your application.

Places to use loading images

There are two ways that I’ve seen these images commonly used.

  1. Use the image to replace the data that is being loaded, in order to indicate that the data is being updated.
  2. Use the image to replace the button that was clicked, in order to show that the action is underway (and to prevent the user from clicking it again right away).

In either case, when the action is triggered, you can use dojo to either hide or replace the data, displaying a loading image instead.

Examples

I set up an example XPage with a view panel that shows 1,000 rows, so there may be a bit of a delay when refreshing the page. I added 2 buttons that put the loading image in place of the view data and 2 buttons that put the loading image in place of the button.

Dojo11_a

The first button uses dojo.place() to both create an image tag and replace the body of the view panel with it. This displays the loading image (which is an animated gif, although you can’t see that by a static screen shot below) in place of the view data, so it’s clear that the data is being refreshed.

dojo.place('<img src="http://www.fluendo.com/media/images/loading_gif.gif" />', dojo.byId("#{id:viewPanel1}"), "replace");

Dojo11_b

The second button produces the same display, but in a different way. It creates the image and puts it before the view data, then hides the view data.

dojo.place('<img src="http://www.fluendo.com/media/images/loading_gif.gif" />', dojo.byId("#{id:viewPanel1}"), "before");
dojo.query('#' + dojo.byId("#{id:viewPanel1}")).style('display', 'none');

Dojo11_c

The third button replaces the button itself with a loading image, by creating the image tag and replacing the button with it.

dojo.place('<img src="http://www.xiconeditor.com/image/icons/loading.gif" />', "#{id:button3}", "replace");

Dojo11_d

The fourth button creates the loading image and replaces the button text with it, but using the innerHTML property, putting the image within the button.

dojo.byId("#{id:button4}").innerHTML = '<img src="http://www.xiconeditor.com/image/icons/loading.gif" />';

Dojo11_ePNG

Image Source

The examples shown above all include full URLs to some loading images online. I did this so you could easily test the code. However, I would strongly suggest adding the image to your application and referencing it relatively. Depending on your network latency and server performance, you may not even see the loading image if it takes longer to load than the data does! It’s much more effective to show it without a delay.

If the image is in the application and still not loading quickly enough, you can try to pre-load it onto the page and hide it, so that it’s cached.

Additional Thoughts

You can combine these techniques and replace both the button and view body if you’d like. (Another option would be to use the loading image in the view body, but do something to change the styling of the button to show that it has been clicked.)

This will only work as expected if the page is either fully refreshed or if it’s a partial refresh that includes whatever you’ve replaced with the loading image. For example, if your partial refresh target is the view body, then the view data will refresh, but the button may not reload and will continue displaying as a loading image.

You can fix this by either including the button in the partial refresh area or by running code on load of the view panel to hide the loading image and re-display the button. Using the replacement-type display of the loading image would not be good in this case, because the original button would be gone.


Viewing all 216 articles
Browse latest View live