Thursday, November 24, 2016

Right Side Column Page and Validation Error Message Region

If you ever used the "Right Side Column" page template, you might have noticed that the validation error message region is displayed under the right side region's expand/collapse trigger button.

As you can see above, the expand/collapse trigger button is on top of the close notification "X" button.

There are two ways to go in order to fix that, both using CSS only.

The first method is to add some right margin to the notification region.

.js-rightExpanded .t-Body-content .t-Body-alert,
.js-rightCollapsed .t-Body-content .t-Body-alert {
    margin-right: 40px; /* Width of the right side region's trigger button */

It will then look like this:

The second method is to reposition the notification region on top of the right side region's trigger button.

.js-rightExpanded .t-Body-content .t-Body-alert,
.js-rightCollapsed .t-Body-content .t-Body-alert {
    position: relative; /* Need to position the div for the z-index to work */
    z-index: 500; /* z-index of right side column is 490 */

It will then look like this:

Interactive Report Alternating Rows

For classic reports there is a region attribute template option to have the rows have alternating row color.

That template option is not part of the interactive report template options.

We can achieve the same effect in an interactive using once again some CSS.

.customAlternatingRow .a-IRR-table tr:nth-child(odd) td {
    background-color: yellow;

.customAlternatingRow .a-IRR-table tr:nth-child(even) td {
    background-color: yellowgreen;

The only thing left to do is to add the "customAlternatingRow" class to your region (under Appearance, CSS Classes) or at the page level.

It will then look like this:

The classic report is using   #fcfcfc (very light gray) for the odd rows and nothing for the even rows.

.customAlternatingRow .a-IRR-table tr:nth-child(odd) td {
    background-color: #fcfcfc;

Which would look like this:

If you would like to also change the highlighted/hovered row color, you can use the following CSS:

.customRowHighlight .a-IRR-table tr:hover td {
    background-color: #f9f9f9;

If you would like this to be applied on all interactive reports in your application, simply remove the ".customAlternatingRow" and/or ".customRowHighlight" from the CSS.

Browser Support of the :nth-child() Selector (from w3schools)

Selector Chrome IE Firefox Safari Opera
:nth-child() 4.0 9.0 3.5 3.2 9.6

You can have a look at my Demo Application

Monday, November 21, 2016

Auto Publish All Translations When Importing an Application

When working with translated application, once everything is done in the source application, you need to seed all the translations, then translate what needs to be and then publish all the translations.
When migrating the application, you will have two choices.

  1. Export the source application and export all mapped translated applications. Then import all the application in the other environment
  2. Export the source application only including the translations (export preferences attribute). Then import the application in the other environment. Then publish all translations.

I usually go with the second method.
If the person that migrates the application knows their way around Apex, it's not that big of a deal.
But when it's not the developer that migrates the applications (e.g. when it's the DBAs), it can be more complicated to explain them the complete procedure.
Furthermore, it could happen that the person who imports the application simply forgets to publish the translations afterwards.

I thought it would be great if the option to include translations when exporting the application also published them when importing the application, but unfortunately, it's not the case.

One thing you can do is to use the apex_lang.publish_application API

You can use it just like in the API's documentation example as a script you would need to run after the import process.

But it would be great if that was executed as part of the install process.

Supporting Objects to the rescue!!

Supporting Objects

What we need to do is to create an Installation Script from scratch using the Editor that simply calls the API.

Use below code:

    -- if using apex 5.0+, you can use 'apex_application_install.get_application_id' to retrieve the application id
    -- otherwise, you need to hardcode the application id
    l_app_id number := apex_application_install.get_application_id;
    for i in (select primary_application_id, translated_app_language
                from apex_application_trans_map
               where primary_application_id = l_app_id)
        apex_lang.publish_application(i.primary_application_id, i.translated_app_language);
    end loop;

Then, when you'll export your application, make sure you include the supporting objects and that you export the translations.

Note: Depending on how you will import the application, the "Export Supporting Object Definitions" attribute has to be set to either "Yes" or "Yes and Install on Import Automatically".

Looking at the attribute's help:
  • Yes Includes supporting object definitions in the application export. Does not automatically load supporting objects when invoked from a command line.
  • Yes and Install on Import Automatically Includes supporting object definitions and a call to install supporting objects in the application export. This option is valid only for command line installs. When application imports from the command line, it automatically installs or upgrades the supporting objects.
So, if you import the application from Apex either will work, but if you import from a command line, you should set the attribute to "Yes and Install on Import Automatically".

When you'll import the application, simply choose to install the supporting objects.

Voila, your application's translations will be automatically published as part of the import process.

EDIT: Added a note regarding the "Export Supporting Object Definitions" attribute.

Monday, November 14, 2016

Required Asterisk Left of Item Label

If you ever had a customer asked you to move the required asterisk of the item's label before the item itself, you probably had to copy the existing template and move the span that holds the asterisk before the item itself then you had to go through all you labels to change the template.

Lately, I've been testing my demo application in the EA of Apex 5.1 and looking at the code and how things changed from 5.0 to 5.1.

Among all the changes, I noticed that the Apex team started to use the CSS3 order property.
The order property specifies the order of a flexible item relative to the rest of the flexible items inside the same container.
Note: If the element is not a flexible item, the order property has no effect.

Basically, this property can reorder items within the same container granted that the container is using the flex property.

To move the asterisk span using only CSS, we can use the following code:
.t-Form-fieldContainer .t-Form-labelContainer {
  display: flex; /* Make sure the container is using flex */
  justify-content: flex-end; /* Keep items right aligned */

.t-Form--labelsAbove .t-Form-fieldContainer .t-Form-labelContainer,
.t-Form--leftLabels .t-Form-fieldContainer .t-Form-labelContainer,
.t-Form-fieldContainer.t-Form-fieldContainer--stacked .t-Form-labelContainer {
  justify-content: flex-start; /* Keep items left aligned */

.t-Form-fieldContainer .t-Form-labelContainer .t-Form-required{
  order: 1; /* Asterisk as first element */

.t-Form-fieldContainer .t-Form-labelContainer .t-Form-label{
  order: 2; /* Label as second element */

You can have a look at my Demo Application

EDIT1: Fix an issue with above labels.

EDIT2: Fix an issue with "Blank with Attributes" region template.

EDIT3: Apex 5.1 now handles the required asterisk using CSS only and the position now depends on the label's alignment.
Right aligned: asterisk before
Left aligned: asterisk after

Friday, November 4, 2016

Handling Dialog Cancel Event

If you ever had to handle a modal dialog close to trigger some action on the parent page, you probably used a dynamic action using the "Dialog Closed" event.

When closing a modal dialog there are two ways to do it.
  1. Close
    • Using an after submit process of type "Close Dialog";
    • Using a dynamic action with a "Close Dialog" action;
    • Branching to a page that has a "Normal" page mode.
  2. Cancel
    • Using a dynamic action with a "Cancel Dialog" action;
    • Clicking on the top right "X" icon/button.

When using a branch to close the modal dialog, the modal dialog will close then it's parent will branch to the specified page.

Except when using the branch method, when you close the modal dialog, Apex will trigger a "Dialog Close" event. Then, on the parent page, you will be able to handle it using a dynamic action "Dialog Closed" event.
The thing is that the cancel methods will not trigger any event on the parent page. So, by default, you won't be able to handle the dialog close when it was cancelled.

One way to have both the close and cancel method trigger an even on the parent page would be to override the dialog close event (Apex is using jQuery's  UI Dialog Widget).

So what we will do is this.

You will need to define the following JavaScript function (either on the parent pages, on page 0 or in your JavaScript global library file)
function customEvent(event, data){
    apex.event.trigger(document, event, data);

Then, under the modal dialog page attributes, in the dialog section, set the "Attributes" to:
close: function() { customEvent('customDialogClose', {modalPageId: 'MODAL_CLOSE_FIXED'});}

What this will do is that it will trigger (on the parent page) the event "customDialogClose".
The second parameter is a data attribute that could be used to differentiate modals between each other. It can can be useful if you are calling more than one on the parent page or if you want to send data back to the parent page.

Finally, in your parent page you can define a custom dynamic action as follow:
Event: Custom
Custom Event: customDialogClose
Selection Type: JavaScript Expression
JavaScript Expression: document

One thing to note is that the default close event will still be triggered when using the close method.

You can have a look at my Demo Application