Skip to main content

Custom APEX navigation

I

I am not a fan of default APEX navigation with hardcoded lists. It is hard and slow to maintain, sometimes you have to do things twice and it is not easy to transfer this to different environment. Fortunatelly, there is an option to create a navigation list based on your SQL query.


How it works normally

  • You create a new page and during this you specify page position and visibility in navigation menu
  • When you change the page name, icon or page auth scheme you also have to manually alter navigation menu - this is the source of the pain
  • Many times developers forgot to sync page changes with navigation menu which leads to users being confused


How it works with my solution

  • You add page however you like and ignore navigation options
  • You add your page to a page group (or create a new group if needed)
  • You open the service page (910 Navigation, on the screen below) and adjust position in navigation and page visibility
  • When you change the page name, icon or page auth scheme you do nothing else - this is the main advantage for me

If user can access the page through the auth scheme set on page, he/she will see it in the menu. If can't he won't.


^ Supporting page 910 Navigation

I just added 905 page in APEX. Blank page, I just set the group to DASHBOARD. That's it. Now I can see it with [+] button in the middle of the DASHBOARD group, exactly where I want it. I just need to hit Auto Update to apply all changes or the [+] link to process just that one line. Done. Now user can see it in menu.

The overview/report matches the menu on top.


Side not on Page Groups. I consider them very useful. In Application Builder you should try to adjust your primary report to View Report (not the default icon view) and use Format - Control Break by Group. That way you will have all relevant pages close together. I also use this groups to prefill parent in navigation on new pages.


Spoiler alert, few objects needed

To make this work you will need some objects:

I left out some unimportant details to simplify this article. But you can find full object definitions on GitHub as a part of the CORE project.


Navigation table

As a database developers we tends to solve our problems with tables.

CREATE TABLE navigation (
    page_id             NUMBER(6)       CONSTRAINT nn_navigation_page_id    NOT NULL,
    --
    parent_id           NUMBER(6),
    order#              NUMBER(4),
    is_hidden           CHAR(1),
    is_reset            CHAR(1),
    --
    CONSTRAINT pk_navigation
        PRIMARY KEY (page_id)
)
STORAGE (BUFFER_POOL KEEP);
--
COMMENT ON TABLE  navigation IS 'Navigation items';
--
COMMENT ON COLUMN navigation.page_id        IS 'APEX page ID';
COMMENT ON COLUMN navigation.parent_id      IS 'Parent page id for tree structure';
COMMENT ON COLUMN navigation.order#         IS 'Order of siblings';
COMMENT ON COLUMN navigation.is_hidden      IS 'Y = dont show in menu';
COMMENT ON COLUMN navigation.is_reset       IS 'Y = reset/clear all items not passed in url';


Basically all you need is a simple table to store hierarchy between pages and the order of the siblings. Hence the parent_id and order# columns. You don't need to store page name, title, auth scheme... It is redundand. It is a waste of time. You change it on your page and than you have to change it in Navigation? No thanks. I don't store these in a table, I retrieve them from APEX views later. You can also easily sync this table, do bulk changes and track changes.

I added some flag columns to hide some pages from user and to reset/clear page items.


Supporting objects

There are some views based on Navigation table:

  • nav_top used as a navigation source in APEX
  • nav_overview used in nav_top and on Navigation page (explained later)
  • nav_pages_to_add to identify pages missing in navigation table (but existing in APEX)
  • nav_pages_to_remove to identify deleted pages (in APEX, but existing in navigation table)
  • nav_badges for page bages; optional, you might not need that


Setup in APEX

1) Create navigation list

Open Shared Components - Lists - Create. Source is SQL Query and query itself is simple:

SELECT
    lvl,
    label, 
    target, 
    is_current_list_entry,
    image, 
    image_attribute,
    image_alt_attribute,
    attribute01,
    attribute02,
    attribute03,
    attribute04,
    attribute05,
    attribute06,
    attribute07,
    attribute08,
    attribute09,
    attribute10
FROM nav_top
ORDER BY group#, sort_order NULLS LAST;


2) Use it as a navigation source

Open Shared Components - User Interface Attributes - Navigation Menu (or Navigation Bar depending on your needs). Select your list.


3) Adjust template

Check comments for use of the nav_top.attribute* columns:

COMMENT ON COLUMN nav_top.attribute01   IS '<li class="...">';
COMMENT ON COLUMN nav_top.attribute02   IS '<li>...<a>';
COMMENT ON COLUMN nav_top.attribute03   IS '<a class="..."';
COMMENT ON COLUMN nav_top.attribute04   IS '<a title="..."';
COMMENT ON COLUMN nav_top.attribute05   IS '<a ...>  // javascript onclick';
COMMENT ON COLUMN nav_top.attribute06   IS '<a>... #TEXT</a>';
COMMENT ON COLUMN nav_top.attribute07   IS '<a>#TEXT ...</a>';
COMMENT ON COLUMN nav_top.attribute08   IS '</a>...';

I always want a specific look and access to data from nav_top view so I changed the default Navigation Bar template. You can get my CSS from menu_top.css file. You need to upload this file to Static Application Files. Lets create new template.

Open Shared Components - Templates. Clone Navigation Menu (or Navigation Bar). Adjust multiple fields:


Before List Entry

<ul class="NAV_TOP">

Template Definition - List Template Current

<li class="ACTIVE #A01#">
  #A02#<a href="#LINK#" class="#A03#" title="#A04#" #A05#>#A06#<span class="TEXT">#TEXT#</span>#A07#</a>#A08#
</li>

Template Definition - List Template Current with Sublist Items

<li class="ACTIVE #A01#">
  #A02#<a href="#LINK#" class="#A03#" title="#A04#" #A05#>#A06#<span class="TEXT">#TEXT#</span>#A07#</a>#A08#

Template Definition - List Template Noncurrent

<li class="#A01#">
  #A02#<a href="#LINK#" class="#A03#" title="#A04#" #A05#>#A06#<span class="TEXT">#TEXT#</span>#A07#</a>#A08#
</li>

Template Definition - List Template Noncurrent with Sublist Items

<li class="#A01#">
  #A02#<a href="#LINK#" class="#A03#" title="#A04#" #A05#>#A06#<span class="TEXT">#TEXT#</span>#A07#</a>#A08#

Template Definition - Between List Elements - empty

Template Definition for First Entry - all 4 empty

Before Sublist Entry

<ul>

Sublist Entry - Sublist Template Current

<li class="ACTIVE #A01#">
  #A02#<a href="#LINK#" class="#A03#" title="#A04#" #A05#>#A06#<span class="TEXT">#TEXT#</span>#A07#</a>#A08#
</li>

Sublist Entry - Sublist Template Current with Sublist Items

<li class="ACTIVE #A01#">
  #A02#<a href="#LINK#" class="#A03#" title="#A04#" #A05#>#A06#<span class="TEXT">#TEXT#</span>#A07#</a>#A08#
</li>

Sublist Entry - Sublist Template Noncurrent

<li class="#A01#">
  #A02#<a href="#LINK#" class="#A03#" title="#A04#" #A05#>#A06#<span class="TEXT">#TEXT#</span>#A07#</a>#A08#
</li>

Sublist Entry - Sublist Template Noncurrent with Sublist Items

<li class="ACTIVE #A01#">
  #A02#<a href="#LINK#" class="#A03#" title="#A04#" #A05#>#A06#<span class="TEXT">#TEXT#</span>#A07#</a>#A08#
</li>

Sublist Entry - Between Sublist Items - empty

After Sublist Entry

</ul></li>

After List Entry

</ul>


Congratulations

It should be working. Obviously you might not want a top menu, you might want a side menu or mega menu. You certainly might want different colors, fonts... It is all up to you to customize this. Good luck.


Comments

  1. This is a really interesting idea. Most of my apps are smaller, but we're re-tooling a 10-yr-old Apex app that has about 15 pages, so this might be worth the implementation overhead.

    Thanks for sharing!

    ReplyDelete

Post a Comment