Wednesday, June 7, 2017

Auto hide client-side messages

Probably somebody did blog about it but I haven't found it yet.

The Problem

If you didn't modified your Universal theme, by default, success and validation messages appear in the top right corner of a page and you have to close them manually or they disappear when you refresh/submit your page.

It can be confusing when you are on a page with Interactive Grid and you change something repeatedly and successfully - it seems like nothing happened. So let's see how to hide them...

The Solution

It can be done by using theme hooks under apex.message namespace.

How To

If you want that all your client side messages are hidden after 3 seconds you should call this code:
apex.message.setThemeHooks({
  beforeShow: function(pMsgType, pElement$){
    setTimeout(function() {
      $('.t-Alert').fadeOut('slow');
    }, 3000);  
  }
});
Where second parameter in setTimeout function (with value 3000) is number of milliseconds. In my case it's 3 seconds.

If you want to hide only success or error/validation messages you can use pMsgType variable. For example:

apex.message.setThemeHooks({
  beforeShow: function(pMsgType, pElement$){
    if (pMsgType=='success'){ 
   setTimeout(function() {
        $('.t-Alert').fadeOut('slow');
      }, 3000);
    }   
  }
});
For error/validation messages pMsgType parameter equals error.

To apply this code for all pages in application put this in Page Load dynamic action on global page (page 0) or in your custom JS file (preferred).

Demo is available here.

Enjoy!

Tested on APEX 5.1.1.00.08

Friday, April 28, 2017

How to make any table header sticky

The Problem

If you're using Interactive Reports and Grids in your apps you may know that there's a property Fixed To that fixes the report's column headers to the top of the page or region so that the column headers remain visible as the user vertically scrolls the report. But there's no such option for Classic report regions or your custom HTML tables.

The Solution

You can easily do it by using two JS widgets that are build into APEX - Sticky Table Header and Sticky Widget.

How To 

To add it to the Classic report region you have to create After Refresh dynamic action that fires after refresh of your Classic report region:


and define true action that executes the following JavaScript code:

var vRegion$ = $(this.triggeringElement);
vRegion$.setTableHeadersAsFixed();
vRegion$.find('.js-stickyTableHeader').stickyWidget();
Dynamic action should look like this:



Remember to set property Fire on Initialization to Yes.

For the additional options you can check out the file libraries/apex/widget.stickyWidget.js in APEX installation folder or through page source view when you're running your application in debug mode.

For custom HTML tables you can use the same principle and if your custom HTML table is not rendering dynamically fire the JS code above only on page load.

The demo is avaliable here.

Enjoy!

Tested on APEX 5.1.1.00.08

Thursday, April 6, 2017

Auto format number items in APEX

One of the questions that I get constantly is how to make a number format masks live - to format number fields as you type or when you leave an item.

In APEX you can define a number format masks in property Format Mask of number fields:


But it's only affected on page load and after page submit for validating format. There's nothing that prevents end user to enter a string or a number in wrong format.




There's a great JS library for that - autoNumeric.js. So let's see how can you implement it in your apps.


2) Unzip the file and upload autoNumeric.min.js (from dist folder) to your workspace (or application) files (Shared Components > Static Workspace/Application Files) or to your server file location.


3) Add reference to the file in your application. I usually do it under the Shared Components > User Interface Attributes > Desktop > JavaScript File URLs


Now you can use it in your APEX apps. To use it for all APEX number type items you can create onLoad dynamic action on the global page (in most apps this is page 0). Number type items have CSS class number_field so you can initialize AutoNumeric on it, for example:


To explicitly override defaults from the global page you can create onLoad dynamic action on the specific page (remember to put greater sequence id from the one on the page 0) and use the update method to change the default properties:


For other methods and properties see autoNumeric.js documentation.

You can see demo here.

Edit (Thanks to Jorge Rimblas): You don't have to hardcode group and decimal separators, you can get them with apex.locale.getGroupSeparator() and apex.locale.getDecimalSeparator().

Enjoy!

Tested on APEX 5.1.1.00.08




Friday, March 17, 2017

APEX 5.1 Master detail (with Interactive/Classic Detail Report)

You probably know that in APEX 5.1 you can easily make a master-detail report regions declaratively by using an Interactive Grids (IG).

But what if you don't want all those fancy IG stuff (I know, I know...you can hide/disable all of it) but plain old Classic (or Interactive) Report for a detail report. No problem, it's only few clicks away!

Let's take a well known example with the dept and emp tables.

First of all you have to create an Interactive Grid over the dept table (master):














Don't forget to put the DEPTNO into SQL Query. It can be hidden, but you have to put it in.

Next step is to create a hidden item (with Value Protected property set to No) where you will temporary store the DEPTNO of the selected grid row (in my case it's P28_DEPTNO):



Then you can create your detail report (Classic or Interactive) over the emp table with where condition that references your hidden item (don't forget to put it in Page Items to Submit):



Next step is to create a dynamic action with event Selection Change [Interactive Grid] over the dept region:


For first true action use Set Value with the Set Type Javascript Expression and define it as:

this.data.selectedRecords.length != 1 ? '': this.data.model.getValue( this.data.selectedRecords[0], "DEPTNO")
and for an Affected element choose your hidden item:



The second true action should be a refresh of your detail region...and thats all.

Live demo is available here.

You can use similar approach if you want this to work with a multiple selected rows (for example in editable grid) but then you should change Set Value DA and where condition of your emp query to support this.

Enjoy!

Tested on APEX 5.1.0.00.45

Friday, March 3, 2017

APEX 5.1 quick fix: Closing modal page before confirm/alert dialog

When I have to do some things repeatedly it’s good indication that I should share this in a blog post. This time it's quick one and I’m sure that it will be fixed by Shakeeb in next the APEX (Universal theme) version.

There’s a problem in APEX 5.1.0.00.45 UT when you call confirm/alert dialog with apex.message JS API from modal page. The problem is that you can close modal page before closing confirm/alert dialog.

You can fix this by adding this CSS block to your custom CSS files or to Theme Roller’s Custom CSS property:
body .ui-front ~ .ui-front{
  z-index:902     
}
Demo is available here.


Enjoy!

Friday, February 24, 2017

How to create simple local web server for APEX static files

Today I've run into a problem that happens to me quite often. I've started a new project at the new client and one of the first things that I've had to do is to modify Universal Theme to meet their corporate design. They had simple APEX architecture with the EPG and without any custom CSS or JS in their apps.

At first, I thought that I would have to do some minor changes in CSS so I've started to use Custom CSS property in Theme Roller. It's great tool, but as I had to do more and more changes it was just too slow.

So I've asked them to provide me with some Web repository where I can easily put and change my files. Because this is a quite big company (actually a bank) with very strict security policies I knew that this would take a too looooong time. So I've started to look for an alternative.

Off course, my first thought was a great tool named APEX Front End Boost (if you don't know it, check it out), but again, because strict security policies, I wasn't been able to install it on the PC that I was working on. The good thing was that I had Node.js already installed there.

So I've googled around for other solutions, but almost every solution was starting like this:
npm install ...
and it was a bummer because I couldn't install anything from npm.

So I've decided to build my own solution. I thought, I'm not a Node.js developer but I know JavaScript so it shouldn't be too hard...and it wasn't.

I've found one simple server script and added some additional stuff like Access-Control-Allow-Origin and Content-Type headers:
var fs = require('fs');  
var path = require('path');  
var http = require('http');
var staticBasePath = '.';

var staticServe = function(req, res) {  
    var fileLoc = path.resolve(staticBasePath);
    fileLoc = path.join(fileLoc, req.url);

    fs.readFile(fileLoc, function(err, data) {
        if (err) {
            res.writeHead(404, 'Not Found');
            res.write('404: File Not Found!');
            return res.end();
        }
  
  var vExt = req.url.split('.').pop(), vContType;
  switch(vExt) {
   case 'css':
    vContType = 'text/css';
    break;
   case 'js':
    vContType = 'text/javascript';
    break;
   case 'png':
    vContType = 'image/png';
    break;    
   default:
    vContType = 'text/plain';
  } 

  res.setHeader('Content-Type', vContType);
  // Chrome Fix
  res.setHeader('Access-Control-Allow-Origin', '*');
        res.statusCode = 200;
        res.write(data);
        return res.end();
    });
};

var httpServer = http.createServer(staticServe);

httpServer.listen(8085); 
...started it:
node node_server.js
..and voila! I had my static file server running on localhost:8085 (change the port number if you already have something running on 8085) and I could access files through URL like this http://localhost:8085/custom_theme/custom.css (where custom_theme is directory where node_server.js is located).

The next goal was that other developers don't have any problems with the application runtime while I'm doing my CSS changes. So at first, I've created one application item F_THEME_PATH



and application computation (after authentication) to populate it in developement environment (with build option included for developement only)


and the other application computation to populate it on production and test environments which always returns path to workspace images (also defined by build option):

return '&WORKSPACE_IMAGES.custom_theme/';

The idea is - if I'm logged in take files from my local server and for other users take files from APEX workspace files. On test and production evnironments always take files from APEX workspace files.

Because there's a great feature to upload zip file with directory structure to APEX workspace files it was really easy to refresh new version of my files when I was sattisfied with my modifications.  

The last thing was to add reference to my files. You can do this in several places in APEX. I've decided to do it in user interface properties:





Enjoy!

Tested on APEX 5.1.0.00.45 and Node 5.2.0

Tuesday, February 7, 2017

APEX 5.1: Translate HTML5 Client-Side Validation Messages

You may know that in APEX 5.1 there are some new functionalities regarding client-side messaging - for example, the capability to display success or error messages without a full page reload or new JS API for handling client-side messages. There are also enabled HTML5 Form Validations, so you may saw something like this in your apps:



The problem that I had is that the user wanted to translate those messages to the other language (in my case Croatian). I thought - no problem. I'll find those messages in the APEX messages dictionary and translate them to the Croatian. After some exploring I found out that those messages are actually browser depended and you can't translate them (the only option is to install browser in different language).

Fortunately, the APEX development team has thought about this and you can override those messages by defining custom attribute named data-valid-message under the item's Custom Attributes property:



And now the form should look like this:


If you don't want to do this for each and every item in your app you can create On Load Dynamic Action on Global Page (page 0) that looks like this:


which will add this attribute to all required fields.

You can read more about the data-valid-message attribute in apex.item documentation.

Enjoy!

Tested on APEX 5.1.0.00.45