Visibility and Size Managed Components

For no particular reason other than I feel like writing about it, I’ll now describe some minor functionality that crept into the APEX 5.1.1 patch release. Typically a patch release shouldn’t and doesn’t have any new functionality but sometimes there is no better way to fix a bug. This topic is for the advanced plug-in developer and anyone curious about the internal details of APEX JavaScript. No screen shots, no demo app, no sample code, just raw information; sorry.

Some components (a.k.a widgets) need to actively manage the sizes of various DOM elements that the widget is composed of relative to the size of the widget container. For example a bar chart would manage the height and width of each bar adjusting them as the size of the widget changes, which may happen as the size of the browser window changes. Another example is how Interactive Grid manages the width of grid columns. Whenever possible native browser layout rules should be used but sometimes there is no other way than to use JavaScript to set the sizes of widget content. I don’t know of a better term for these things so I’ll call them size managed components. Generally a widget needs to be visible to correctly determine its size.

The purpose of some components is to hide or show parts of the web page, which can include other components. Examples include tabs, collapsibles, and accordions. I’ll call these kinds of things hideables.

When a size managed component widget is inside a hideable widget and is initially hidden when it is initialized you can run into problems. (Think of a chart region inside a collapsible region that is initially collapsed.) This is because the size managed component has no idea what size it is. Depending on how robust the widget is it may throw an exception, or it may be sized in appropriately. A common situation is that it ends up with zero height and/or width so you can’t see it.

APEX has a very simple life cycle for its components (regions and items many of which are implemented with widgets). They are all initialized when the page loads and they are implicitly destroyed when the page unloads. This means for example that when an Interactive Grid was placed in a Tabs Container region it didn’t work and we had a bug.

Over time many bugs like this were entered and fixed. They were all very specific and of the form component X doesn’t work inside component Y. The fixes were likewise specific. By the time a specific bug for Interactive Grid was assigned to me there were hacks in the APEX tabs widget to handle charts, calendars and more. The trouble is that the problem is more general and adding another hack for Interactive Grid wasn’t a complete solution.

In general we have N kinds of size managed components (including ones created by our customers) that need to work inside M kinds of hideables (again they could be created by our customers). The hideables need to notify the size managed components when they become visible so that they can refresh/resize/reinitialize themselves. It is simply not possible to code every kind of hideable so that it knows specifically how to handle every kind of size managed component.

This leads to the new functionality added in 5.1.1. The solution is a new notification API you can find in widget.util.js. It works like this: Size managed components call apex.widget.util.onVisibilityChange when an instance is initialized passing in the widget element and a notification handler function. The notification handler function does whatever it needs to do to refresh or resize the component. (The widget can call offVisibilityChange when it is destroyed.) Hideable components call apex.widget.util.visibilityChange any time the visibility of an element it manages changes. It passes in the element that changed visibility and true if it became visible and false if it became hidden. In this way hideables can notify any components that need to know when they become visible (or invisible) without having to know anything about those components.

Note: do not use this API to detect when a tab is made active or a collapsible is expanded. There are events for those purposes.

So far this has been implemented for Interactive Reports, Interactive Grids, JET Charts, and Calendar and in apexTabs (used by Region Display Selector and Universal Theme Tabs Container region template), Collapsible region template and Show, Hide Dynamic Actions.

If you create a region plug-in (or region template) that can show and hide its contents then you should implement the necessary call(s) to apex.widget.util.visibilityChange so that regions like Interactive Grid or JET Charts can be placed inside it.

If you create a region plug-in that is a size managed component then you should use apex.widget.util.onVisibilityChange so that when the region is placed in a hideable region it can be notified when it is made visible. Keep in mind that the size of the containing hidable may have changed while the region was hidden so the region should resize itself every time it is made visible.

There is an oddity with the Region Display Selector (RDS) in how it handles visibility. Initially the RDS always had a Show All tab and would show all the regions when the page loads. In 5.0 when the options to not have a Show All tab and to remember the selected tab were added it was now possible for components to be initialized while they were hidden in an RDS tab. We started noticing problems with some item types so a partial last minute workaround was added where the RDS would show all the tabs for a brief moment when the page loads so that page items could be properly initialized. Now that we have the visibility change APIs this momentary visibility “solution” is regrettable. If you had a widget that is expensive to initialize you may implement it such that it doesn’t do much if it is invisible and only when it is first made visible does it do the full initialization. The current RDS behavior spoils this optimization. I don’t know what we will do about this.

Another thing to know about if you create size managed component plug-ins is the onElementResize, offElementResize and updateResizeSensors APIs in the apex.widget.util namespace. See the widget.util.js file for details. A simple way to create a widget that dynamically resizes itself to fit the available space is to listen for window resize event (or better the apexwindowresized event). But this doesn’t cover all the cases where the the region should be resized. For example the collapsible navigation side bar used in Universal Theme can leave the main content either too wide or too narrow. The solution is to use onElementResize to be notified when the widget element container size changes. Currently the only example of this is in widget.interactiveGrid.

Disclaimer: None of the above APIs are currently documented or supported. I don’t know if they will be documented and supported in the next APEX release.

2 thoughts on “Visibility and Size Managed Components

  1. I knew there was a reason for seeing sub-regions (aka tabs) “blinking” when page loading! LoL… I don’t remember how many times I’ve set “display:none” for those regions and then changing it to display:block “after” loading the page.

  2. Hi John,

    sorry if my question may be off-topic here…but about interactive grid, is there a way using jquery or other, to target specific cell (not the column) of the grid in order, for example to make it readonly, change color and so on ?

    Thanks , regards

Comments are closed.