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:
- 1 (navigation) table to store data
- 2 simple (nav_add_pages, nav_remove_pages) procedures to manipulate table records
- 5 supporting views (nav_top, nav_overview, nav_pages_to_add, nav_pages_to_remove, nav_badges*)
- 1 supporting APEX page (910 Navigation) for app administrators
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.
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.
ReplyDeleteThanks for sharing!