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

Continue reading

Interactive Grid column widths

Since the first Early Adopter release of APEX 5.1 I have seen a number of questions or complaints about how column widths are handled in Interactive Grid. I think the complaints may be based on misunderstandings of how things work. I hope to explain here everything you need to know about column widths in Interactive Grids. Still, I’m not 100% sure that in implementing Interactive Grid we got it all exactly right. You can let me know in the comments after reading this.

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

Continue reading

Passing Data in and out of APEX Dialogs

How to pass data into and return data from an APEX dialog is something that comes up from time to time on the APEX forum. I have not seen a comprehensive treatment of this topic so decided to work up an example of the various possibilities.

[Updated 23-May-2017 to reflect 5.1.1 and add detail for when dialogs are opened from navigation menu list]

Continue reading

Remove Component View from APEX Page Designer

In the beginning (or soon after) there was Component View. Then in APEX 5.0 we added Page Designer but left Component View around because it had some die hard fans and “just in case”.

Many speculated on what the ultimate fate of Component View would be. Now in 5.1 a new Component View tab has been added to Page Designer and the original Component View is re-dubbed Legacy Component View. Legacy Component view is disabled by default. To enable it go to Account Menu Preferences and set Enable Legacy Component View to Yes. Once you enable it you can find the link to it on the new Component View tab in Page Designer. Most importantly Legacy Component View is deprecated. The Release notes say:

Component View has been designated as a legacy user interface for editing application pages. Oracle recommends developers use Page Designer instead of Legacy Component View. …

So it will likely be removed completely in a future release. Already in 5.1 there are a number of new components like JET Charts and Interactive Grids that can’t be edited with Legacy Component View. Its just too much work to keep both Page Designer and legacy component edit pages up to date and in sync.

The Component View tab provides the familiar page organization of Legacy Component View with the editing efficiency of Page Designer. For people used to Component View we think it is the best of both worlds.

So Component View is dead, long live Component View!

Some may say “Whats the big deal with the new Component View tab? I’ve already moved on and this new tab just gets in my way”. For those people I have a little secret to share (its totally unsupported and it may not work in the future). You can remove the Component View tab from Page Designer by navigating to Page Designer then opening the browser’s console window and typing in the following code:


$("#componentView,[aria-controls='componentView']").remove();pageDesigner.storeTabsLayout();

This is per user and will persist after you log out. To get it back again from Page Designer open the Settings menu and choose Reset Layout and then refresh the page. Refreshing the page is important because just resetting the layout alone won’t bring it back.

Don’t try this with other tabs.

I hope you enjoy this little trick and enjoy APEX 5.1

Interactive Grid: Under the Hood

[This article is about a beta release of APEX 5.1 known as Early Adopter 1 so the details are subject to change]

[UPDATE Jan-2017 APEX 5.1 was released. Don’t use the Early Adopter link. You can now try it out at apex.oracle.com. I made a few updates to this article as marked]

One of the major and eagerly awaited features of APEX 5.1 is Interactive Grid; an editable data table supporting master-detail and much more. You can learn about Interactive Grid by signing up for a workspace at the Early Adopter site, installing the Sample Interactive Grids application, running it and trying out each example. Be sure to read the overview section of each page. I expect in the coming weeks there will be tutorials and other information from a number of people about the features of Interactive Grid and how to use it in various ways. But here I want to talk about its internal architecture.

Why is it important to know about the internal architecture of Interactive Grid? Its not. To use a car analogy most people don’t care to know how their car works. What matters is that it gets them from point A to point B and possibly looks good as well. But some people like to know a little about how the car works, what’s under the hood, even if they never intend to service or build a car themselves. This article is for those who like to know how things work, and I expect there are a fair number of them in the APEX development community.

Continue reading

Using Oracle JET from APEX

This post contains statements about possible future functionality. These are not official statements. They are speculative, subject to change, and should not be relied upon.

Reading svenweller’s recent article Integrate Oracle JET into Apex 5.0 got me thinking about this topic. I’m very busy working on Interactive Grid in APEX 5.1 right now but this is an important topic and there is a need to get some information out sooner rather than later. I’m not going to go into how JET and APEX are great, why it makes sense to use them together or provide step-by-step instructions for doing so. See svenweller’s blog for that.

By now most APEX developers have heard that we plan to include Oracle JET based charts in APEX 5.1. It is generally easy to use 3rd party JavaScript libraries with APEX. Here I want to talk about why Oracle JET is a special case and give some tips for making them work together. I want to share what we have learned so far in integrating JET charts with APEX. I should mention that I am not the main person working on the chart feature.

What makes using Oracle JET with APEX tricky is that JET requires you to use RequireJS. So the same issues apply if you were to integrate any code that uses RequireJS with APEX. It is not specific to Oracle JET. Furthermore, you would have similar issues trying to use JET with any traditional web app that isn’t already using RequireJS.

Is RequireJS bad? No. Is APEX somehow at fault? No. What’s going on here? Its complicated…

The traditional way of using JavaScript on a web page is to put it in script tags and call it. Lots of libraries? No problem. Just figure out what order to put all the script tags in. Each script file adds some global symbols for the others to use. Namespace objects are used to reduce the number of globals — its a kind of modularity. This is what APEX does. It is conceptually simple.

Then modules were invented as a way to keep code, well, modular and explicitly define what your module exports and what dependencies you have on other modules. Just one script tag and the module system (in this case RequireJS) does all the loading. The only globals are from the module system (define, require, requirejs). This is also conceptually simple but different concepts.

There is nothing wrong with either of these ways of building web apps. I have nothing against modules. I like using the Node.js module system. But APEX has a long history and simply cannot switch over to using RequireJS modules without breaking existing applications.

These are two very different worlds that don’t mix well. Its hard to ease into using modules because of what happens when the existing non-module code and the new module code wants to share a library.

If you are the creator of a handy library you want to increase your user base by being usable in both worlds. (I’ll use Hammer.js as the example simply because it is a library that both JET and APEX Universal Theme use.) Its easy to do, just add at the bottom of your code something like the following:

// this is just slightly simplified from the actual hammer.js code
if (typeof define == "function" && define.amd) {
    define(function() {
        return Hammer;
    });
} else {
    window.Hammer = Hammer;
}

Now the library can be used in a module system if the page is using one or just define a global for the traditional way.

But if a single page has some code that uses RequireJS and some code that uses the traditional way and both want to use the same library (Hammer.js in our case) there is a problem.

A) If the hammer.js script tag is loaded after require.js it defines an anonymous module as shown above and then as soon as you do anything else with RequireJS you get the error: Mismatched anonymous define() module. The reason is that RequireJS insists that all anonymous modules are loaded from a define or require call. The assumption is that as soon as RequireJS is on the page you are using the new modular way.

B) If the hammer.js script tag is loaded before require.js it defines its global Hammer object so that traditional code can use it. Then when module code uses require(['hammer'...]...) or something equivalent it will get a new copy of the Hammer object. You can prove this with the following code:

require(["hammerjs"], function( h ) { console.log( h === window.Hammer );});

If the the new module code and the global Hammer object were the same it would print true but it prints false. At least there is no error and it seems to work but… Ideally we would like to avoid this. Depending on what the library does it could result in more than just wasted memory.

We had a similar problem with jQuery where after RequireJS was used by JET Charts all our APEX jQuery UI widgets were essentially “gone” because there were two different jQuery objects ( $ !== apex.jQuery ). Note jQuery is a special case in that it defines a named, not anonymous, module so it doesn’t suffer from the error in case (A). You may also say that it is best to always use apex.jQuery rather than $ and you are correct but not everyone follows this rule all the time. And we still don’t want to waste memory on duplicate copies of libraries.

With this background out of the way here are my recommendations.

  • Load require.js as late as possible because problem B is much easier to deal with than problem A. (How to deal with problem B is coming up.)

    RequireJS would like to be the first (and only) script tag on the page but it doesn’t have to be. In order to keep any RequireJS aware libraries such as hammer.js or jQuery from calling define, require.js should be included after them. The best way to do this is to add require.js to a page in page attribute JavaScript: File URLs.

    With this approach you don’t need to throw out hammer.js just to use Oracle JET. Hammer.js is useful when running your app on touch devices.

  • You don’t need to use data-main on the require.js script tag. It is not required, just a convenience. Your code that includes the call to requirejs.config can be handled like any other JavaScript code. If you put it in a file, reference that file in JavaScript: File URLs (after require.js). It can even go inline by putting it in JavaScript: Execute when Page Loads.
  • To get around the problem of loading multiple copies of a library add the following code after the call to requirejs.config and before any calls to require
    define( 'hammerjs', [], function() {
        return Hammer;
    });
    define( 'jquery', [], function() {
        return apex.jQuery;
    });
    

    What this does is define the library module with a name in the RequireJS module system so that it will not be loaded again. It is important to use the same name that the library uses in the config paths object passed to requirejs.config. This can be extended to other libraries if needed.

    Note: If different versions of a library are needed then you may just have to let two copies be loaded. In this case remove the corresponding call to define.

  • You probably don’t need to use knockout. I don’t know for sure if this is true for all JET components but it is true for JET Charts. APEX 5.1 is not using knockout at all. I have nothing against knockout. However, it is of most benefit if it is used pervasively through out an app. Otherwise it is just another another library to load for which there are other ways to do what it does. That said, if you want to use it go ahead.

At this point I don’t know what if anything APEX 5.1 will do to make integrating Oracle JET (or RequireJS in general) easier. For now I hope this helps anyone trying to use JET with APEX 5.0.