How to hack APEX Interactive Grid Part 1

It is nice to see from the questions rolling in on the APEX forums that people are trying to do interesting and advanced things with Interactive Grid (IG). I’m happy to answer questions about how to do things programmatically with IG but in this 3 4 part series I hope to give you some tools, tips and examples to figure things out on your own. This blog series assumes at least intermediate level experience with APEX and JavaScript.

[Update 5.1.1 30-Mar-2017] This article has been updated to reflect patch release 5.1.1

The Basics

Install and go through the Sample Interactive Grids application reading all the Overview sections. There are plenty of examples of using advanced configuration options; and not just in the Advanced section. For example the Reporting > Sequence Row Header page uses advanced configuration to add a sequence number to the row header.

It helps to have a general idea of the IG architecture if you are going to program it with JavaScript. At a minimum you need to know that IG is actually made up of many widgets and has a data model layer. What you want to do will determine which parts of IG you need to work with. If you want to enhance the toolbar or menus of IG then you will be dealing with actions and the menu and toolbar widgets. If you want to access or manipulate the data across rows you will be dealing with the APEX model.

Realize that many of the JavaScript APIs are not documented or supported. This mainly applies to all widget APIs as well as apex.actions and apex.model. You will need to read doc comments in the JavaScript source files to learn more about these APIs. Doc comments start with /** and include tags such as @param. Also useful in the widget modules are the comments after each of the default options properties. It is not clear if, how or when these APIs will be documented. But it is not all bad news. Some new APIs are documented and supported for example the apex.region namespace. The apex.message namespace will hopefully be documented in the near future; we just ran out of time is now also documented.

To read doc comments in JavaScript source files run the app from the APEX App Builder. In the developer toolbar click Debug. This will reload the page with JavaScript files separated and not minified. Then you can use the Browser’s developer tools network tab to find the URLs of JavaScript files such as widget.grid.js and open them in a new tab. If you have your own copy of APEX installed and have access to the web server you can look at the files in the images/libraries/apex folder.

Configuration

As you probably already know APEX regions provide a wrapper around a JavaScript widget. The wrapper provides the integration with the declarative facilities of APEX. Increasingly these widgets are based on jQuery UI Widget Factory which means they all have a similar feel in how you configure and control them and respond to their events. Generally UI widgets, either 3rd party ones like JET Charts or fullcalendar or our own grid, have more configuration options than would be reasonable to expose as declarative attributes. We pick the most useful options to expose as attributes to make APEX easy to understand and use while still supporting all the common use cases. But there are always people that want more control over what to configure. In the past it has been difficult to customize widget options.

Starting with Interactive Grid, and also used by other region types such as Chart and Calendar, a new common pattern for advanced configuration of the underlying widget is provided. There is an Advanced attribute called JavaScript Code. This attribute takes a JavaScript function with the following form:

function(options) {
    // update options here
    return options;
}

See the Page Designer help tab for region specific details.

The region plugin (even native regions like IG are basically plugins) is responsible for creating the initial configuration (or options) object for the widget based on the declarative attributes and then it passes that object to the function you provide. You can add to or modify the options object and return it. Use the browser view source to see and understand how your function is called. Keep in mind that any any syntax errors can break the whole page so when editing any JavaScript code run the page with the JavaScript console open.

To understand what option properties can be set, their values and what they mean consult the documentation of the underlying widget. In the case of APEX widgets this means reading the source files. Unfortunately the options for widget.interactiveGrid are not well documented in the source at this time. You will have to make an educated guess at what a property does based on its name and trial and error.

A nice way to explore the options in general is to do this:

function(options) {
    console.log("options: ", options);
    return options;
}

With IG there are so many options the server lets the client provide most defaults. So the above technique doesn’t give the complete picture. To get a more complete picture of the available options for IG type the following into the browser’s console.

apex.region("myregionid").widget().interactiveGrid("option").config

This shows the config options after all the defaults have been applied. Substitute the actual Static ID you gave the region for myregionid. Change various attributes to see how they affect the widget configuration.

Notice the use of the new apex.region API. Before APEX 5.1 it was common to get access to a widget using jQuery code such as $("#regionStaticId_ir").interactiveReport(...) This relied on internal details of how the region generated element ids. In this case by adding an “_ir” suffix. This new region namespace gives a much cleaner official way of working with regions without having to rely on internal markup details.

Tip for region plugin developers: Implement the region interface for your region plugins by calling apex.region.create. If there is an underlying widget implement the widget method. See the documentation for details.

The JavaScript Code function for Interactive Grid only receives the config property of the widget options. The IG widget options includes much more including report settings and data that are not related to configuration. You don’t have access to those options.

Advanced configuration for IG columns works in a similar way.

[Update 5.1.1] To pass column configuration options on to the grid widget use property defaultGridColumnOptions. Its value is an object with the options to pass on to the grid. For example, the following function added to the column Advanced: JavaScript Code attribute will disable the column header popup menu for that column.

function(config) {
    config.defaultGridColumnOptions = {
        noHeaderActivate: true
    };
    return config;
}

Tip for plugin developers: Feel free to copy this Advanced JavaScript Code pattern to configure advanced widget settings.

If your application has many Interactive Grids that need to be configured similarly don’t copy and paste the same function from one IG to the next. Rather put the code in a static application JavaScript file loaded for all pages. See this nice animation of how to provide JavaScript that is loaded on all pages.

For example you could upload a static application JavaScript file called myApp.js with this content:

function customGrid(config) {
    // update config object here
    return config;
}

Then in the Advanced JavaScript Code attribute for all the applicable IG regions you would simply put customGrid. (Note that it is not customGrid().) If you have a few different kinds of IG regions you can create different functions one for each kind.

Here is a neat trick that lets you handle different configurations for different IG regions based on a region CSS class. It takes advantage of the fact that the config object passed to the function includes the region static id. For example you can have the following static JavaScript file loaded on all pages.

(function($) {
window.myApp = {
    commonIGConfig: function(config) {
        config.defaultGridViewOptions = {
            stickyFooter: false
        };
        if ($("#" + config.regionStaticId ).hasClass("seqNo")) {
            config.defaultGridViewOptions.rowHeader = "sequence";
        }
        if ( config.pagination.type === "set" ) {
            config.defaultGridViewOptions.pagination = {
                firstAndLastButtons: false,
                maxLinks: 12
            };
        }
        // other config changes here possibly conditional on other classes
        return config;
    }
};
})(apex.jQuery);

Then add myApp.commonIGConfig to all IG Advanced: JavaScript Code attributes. Now for any IG regions that have a Row Selector column and you want to have a sequence number displayed simply add the class seqNo in the region’s Appearance: CSS Classes attribute. You can add conditional configuration for any number of classes. This technique essentially creates a menu of options that you can select simply by adding CSS Classes. If you have an unlocked theme you could even tie this into template options.

This example always makes the footer not stick to the bottom of the page. It also shows that settings can be made based on other settings. In this case if the attribute Pagination: Type is “Page” (resulting in pagination.type having value “set”) then the grid will not have first and last pagination buttons and will show 12 links.

Note the above example uses a self invoking function to provide a scope for private variables and functions. It passes in apex.jQuery so that the private $ symbol will refer to the same jQuery that APEX uses. It creates a namespace myApp so all the application functions will not pollute the global namespace.

This has been an overview of IG configuration. You should now have enough information to explore the various configuration options on your own. In the next part of this series I’ll cover toolbar and menu customization.