Logo Oracle Deutschland   Application Express Community
APEX Tabs als Pulldown-Menü: wie im Application Builder
Erscheinungsmonat APEX-Version Datenbankversion
Juni 2012 ab 4.0 ab 10.2

Einleitung: Worum geht es?

Jeder kennt die Reiterkarten im APEX Application Builder, mit der eleganten Möglichkeit, das Untermenü als Pulldown-Menü aufzuklappen (Abbildung 1). Und viele fragen sich, wie man sowas in eigenen APEX-Anwendungen verwenden könnte.

Reiterkarten als Pulldown-Menü im APEX Application Builder

Abbildung 1: Reiterkarten als Pulldown-Menü im APEX Application Builder

Schön wäre es dann noch, wenn dann auch mehr als eine Hierarchieebene unterstützt würde ...

Reiterkarten als Pulldown-Menü mit mehreren Hierarchieebenen

Abbildung 2: Reiterkarten als Pulldown-Menü mit mehreren Hierarchieebenen

Spätestens dabei wird klar, dass die APEX Reiterkarten (Tabs) hier nicht in Frage kommen, denn diese unterstützen nur zwei Ebenen - aber auch sonst haben APEX-Reiterkarten noch den einen oder anderen Nachteil. So senden Sie bei Klick zwingend ein Page Submit ab - das einfache Ausführen eines JavaScript-Calls und das Verbleiben auf der aktuellen APEX-Seite ist mit Tabs nicht möglich. Tabs verzweigen außerdem stets auf eine APEX-Seite - andere URLs sind nicht möglich.

Im Internet findet sich ebenfalls der eine oder andere Tipp zum Thema; allerdings basieren viele dieser Tipps auf den JavaScript-Funktionen, die auch der Application Builder intern verwendet. Allerdings sind diese nicht dokumentiert - man kann sich also nicht darauf verlassen, dass der Ansatz in künftigen APEX-Versionen noch funktioniert. Besser ist es also, eine Lösung zu erstellen, die keinerlei Abhängigkeiten zu undokumentierten Funktionen hat. Dieser Tipp stellt eine Lösung auf der Basis von APEX-Listen vor.

Listen sind eine gute Alternative zu Reiterkarten. Listeneinträge können beliebig geschachtelt werden, auf beliebige URL-Ziele verweisen, beliebig oft auf einer Seite eingesetzt werden und mit Templates beliebig gestaltet werden. Und wie die Reiterkarten kann ein Listeneintrag mit einer APEX-Seite verknüpft werden, so dass APEX weiß, ob man sich aus Sicht des Listeneintrags auf der "aktuellen" Seite befindet.

Im Tipp beschäftigen wir uns im wesentlichen mit dem Listen-Template - dieses wird so gestaltet, dass am Ende ein Pulldown-Menü - wie im APEX Application Builder - herauskommt. Den Tipp und die beschriebene Vorgehensweise können Sie dann auch für die Gestaltung eigener Templates nutzen.

Liste erzeugen

Bevor Sie mit dem Schreiben von HTML-, CSS und JavaScript-Code loslegen, braucht es eine einfache Liste zum Testen. Navigieren Sie also zu den Gemeinsamen Komponenten und erzeugen Sie sich eine Liste mit folgender Struktur. Als Listentemplate wählen Sie zunächst DHTML Tree aus.

  • 1
  • 2
    • 2.1
      • 2.1.1
      • 2.1.2
      • 2.1.3
    • 2.2
      • 2.2.1
      • 2.2.2
  • 3
    • 3.1
    • 3.2
      • 3.2.1
        • 3.2.1.1
        • 3.2.1.2
      • 3.2.2

Abbildung 3 zeigt die Übersicht über die erstellten Listeneinträge im Bereich Listen in den Gemeinsamen Komponenten.

Die geschachtelten Listeneinträge wurden erzeugt

Abbildung 3: Die geschachtelten Listeneinträge wurden erzeugt

Erzeugen Sie sich dann eine Anwendungsseite mit einer neuen Region vom Typ Liste. Wählen Sie dann die neue Liste aus und starten Sie die Seite. Je nach dem von Ihnen verwendeten Theme sieht die Seite dann wie in Abbildung 4 aus.

Anwendungsseite mit Liste und Template "DHTML Tree"

Abbildung 4: Anwendungsseite mit Liste und Template "DHTML Tree"

Nun geht es an das Erstellen des Templates. Um ein Aussehen wie in Abbildung 2 hinzubekommen, braucht es etwas mehr, als eine einfache Schachtelung der HTML-Tags <ul> und <li>. Zusätzlich sind noch einige "DIV-Container" erforderlich, welche bspw. den kleinen Pfeil zum Aufklappen des Menüs sowie einige CSS-Angaben enthalten. Navigieren Sie also zu den Gemeinsamen Komponenten , dort zu den Templates und erstellen Sie dann ein neues Listen-Template namens PullDownMenu. Erstellen Sie es Völlig Neu.

Neues Listen-Template erstellen

Abbildung 5: Neues Listen-Template erstellen

Danach werden Sie zur Übersicht über alle Templates zurückgeleitet. Suchen Sie das neue Template heraus und klicken Sie es an.

Erstelltes Template heraussuchen

Abbildung 6: Erstelltes Template heraussuchen

Abbildung 7 zeigt den Dialog, in dem Sie dann die einzelnen Teile des Listentemplates pflegen können. Es gibt eigene Bereiche für Listeneinträge mit und ohne Unterelemente, für Listeneinträge der der aktuellen Seite entsprechen, für den jeweils ersten Listeneintrag und einiges mehr.

Im Template werden nun die HTML-Fragmente hinterlegt

Abbildung 7: Im Template werden nun die HTML-Fragmente hinterlegt

Tragen Sie folgendes in die verschiedenen Abschnitte der Template-Definition ein.

  • Template vor Zeilen auflisten im Bereich 'Vor'-Listeneintrag
    <ul class="cczdhtmlmenuull1">
    


  • Listen-Template (aktuell) im Bereich Template Definition
    <li id="#LIST_ITEM_ID#" class="cczdhtmlmenulil1 cczdhtmltopcurrent">
     <div class="cczdhtmlmenutextl1"><a href="#LINK#" title="#TEXT_ESC_SC#">#TEXT#</a></div>
    </li>
     
  • Listen-Template (aktuell) mit Sub-Listenelementen im Bereich Template Definition
    <li id="#LIST_ITEM_ID#" class="cczdhtmlmenulil1 cczdhtmltopcurrent">
     <div class="cczdhtmlmenutextl1"><a href="#LINK#" title="#TEXT_ESC_SC#">#TEXT#</a></div>
     <div class="cczdhtmlmenuhandle">&nbsp;</div>
    </li>
    
  • Listen-Template (nicht aktuell) im Bereich Template Definition
    <li id="#LIST_ITEM_ID#" class="cczdhtmlmenulil1 cczdhtmltopnoncurrent">
     <div class="cczdhtmlmenutextl1"><a href="#LINK#" title="#TEXT_ESC_SC#">#TEXT#</a></div>
    </li>
    
  • Listen-Template (nicht aktuell) mit Sub-Listenelementen im Bereich Template Definition
    <li id="#LIST_ITEM_ID#" class="cczdhtmlmenulil1 cczdhtmltopnoncurrent">
     <div class="cczdhtmlmenutextl1"><a href="#LINK#" title="#TEXT_ESC_SC#">#TEXT#</a></div>
     <div class="cczdhtmlmenuhandle">&nbsp;</div>
    </li>
    


  • Sub-Listen-Template (vor Zeilen) im Bereich Vor Sub-Listeneintrag
    <ul id="#PARENT_LIST_ITEM_ID#" class="cczdhtmlmenuull2"  htmldb:listlevel="#LEVEL#">
    


  • Sub-Listen-Template (aktuell) im Bereich Sub-Listeneintrag
    <li>
     <div class="cczdhtmlmenutextl2">
       <a href="#LINK#" title="#TEXT_ESC_SC#">#TEXT#</a>
     </div>
    </li>
    
  • Sub-Listen-Template (aktuell) mit Sub-Listenelementen im Bereich Sub-Listeneintrag
    <li>
     <div class="cczdhtmlmenutextl2">
       <a href="#LINK#" title="#TEXT_ESC_SC#">#TEXT#</a>
     </div>
     <div class="cczdhtmlmenuhandle">&nbsp;</div>
    </li>
    
  • Sub-Listen-Template (nicht aktuell) im Bereich Sub-Listeneintrag
    <li>
     <div class="cczdhtmlmenutextl2">
       <a href="#LINK#" title="#TEXT_ESC_SC#">#TEXT#</a>
     </div>
    </li>
    
  • Sub-Listen-Template (nicht aktuell) mit Sub-Listenelementen im Bereich Sub-Listeneintrag
    <li>
     <div class="cczdhtmlmenutextl2">
       <a href="#LINK#" title="#TEXT_ESC_SC#">#TEXT#</a>
     </div>
     <div class="cczdhtmlmenuhandle">&nbsp;</div>
    </li>
    


  • Sub-Listen-Template (nach Zeilen) im Bereich Nach Sub-Listeneintrag
    </ul>
    
  • Template nach Zeilen auflisten im Bereich 'Nach'-Listeneintrag
    </ul>
    

Speichern Sie das Template ab, wenn Sie fertig sind und navigieren Sie wieder zu Ihrer Anwendungsseite. Nun muss das neue Template in der Listenregion eingestellt werden. Navigieren Sie dazu zu den Regionsattributen der Listenregion und stellen Sie das Listentemplate, wie in Abbildung 8 dargestellt, um.

Listentemplate in der Regionsdefinition umstellen

Abbildung 8: Listentemplate in der Regionsdefinition umstellen

Danach sieht die Anwendungsseite in etwa wie in Abbildung 9 aus. Man sieht ... das sich scheinbar nichts verändert hat. Kein Wunder, denn im Listentemplate wurden nur HTML-Tags wie <ul>, <li> und <div> verwendet. Es finden sich keinerlei Layout-Definitionen oder Styling-Informationen darin. Die verwendeten HTML-Elemente tragen lediglich das Attribut class, welches eine oder mehrere CSS-Klassen referenziert.

Anwendungsseite nach Umstellung des Listentemplate - Blick ins HTML mit dem "Firebug"

Abbildung 9: Anwendungsseite nach Umstellung des Listentemplate - Blick ins HTML mit dem "Firebug"

Als nächstes gilt es also, genau diese CSS-Klassen in die APEX-Anwendung zu bringen. Erstellen Sie mit dem folgenden CSS-Code eine Datei namens PullDownMenu.css und laden Sie diese bei den Gemeinsamen Komponenten unter Cascading Style Sheets hoch.

div.cczdhtmlmenutextl2 {
  padding: 2px;
  margin: 0px;
}

div.cczdhtmlmenutextl1 {
  margin: 10px 2px 2px 10px;
}

div.cczdhtmlmenutextl1 a {
  text-decoration: none;
  color: white;
  font-size: 16px;
  color: white;
} 

div.cczdhtmlmenutextl2 a {
  text-decoration: none;
  color: white;
  font-size: 14px;
  color: #606060;
} 


.cczdhtmltopcurrent {
  background-color: #f00000;
}
.cczdhtmltopnoncurrent {
  background-color: #800000;
}

li.cczdhtmlmenulil1 {
 float: left;
 position: relative;
 width: 180px;
 height: 35px;
 padding: 0px;
 margin: 0px;
 border-right: 1px solid white;
}

ul.cczdhtmlmenuull1 {
 position: absolute;
 height: 35px; 
 list-style: none outside none;
 margin-left: 0px;
}


ul.cczdhtmlmenuull2 {
 list-style: none outside none;
 width: 180px;
 margin-left: 0px;
 border: 1px solid black;
 background-color: white;
 position: absolute;
 display: none;
}

ul.cczdhtmlmenuull2 li  {
 position: relative;
 width: 100%;
 color: #202020;
 font-size: 14px;
}

div.cczdhtmlmenuhandle {
  position: absolute;
  cursor: pointer;
  width: 20px;
  height: 20px;
  right: 0px;
  background-repeat: no-repeat;
}

.cczdhtmlselectedNode {
  background-color: #dddddd;
  font-color: white
}

Navigieren Sie anschließend zum Seitentemplate und referenzieren Sie das CSS-Stylesheet wie folgt (Abbildung 10).

<link rel="stylesheet" href="#WORKSPACE_IMAGES#PullDownMenu.css" type="text/css" />

CSS-Datei im Seitentemplate referenzieren

Abbildung 10: CSS-Datei im Seitentemplate referenzieren

Nun fehlt aber noch was: Zum Aufklappen des jeweiligen Untermenüs benötigen wir noch sowohl einen kleinen, nach unten zeigenden als auch einen kleinen, nach rechts zeigenden Pfeil. Beide finden Sie in der Datei pulldownmenu-pfeile.zip. Packen Sie das ZIP-Archiv aus und laden Sie beide Bildchen in den Gemeinsamen Komponenten bei den Bildern als Workspace-Bilder hoch (Abbildung 11).

Die Pfeile sind als Workspace-Bilder hochgeladen

Abbildung 11: Die Pfeile sind als Workspace-Bilder hochgeladen

Diese Bilddateien benötigen wir wiederum in den CSS-Anweisungen. Da der Alias #WORKSPACE_IMAGES# in einer bei den Gemeinsamen Komponenten hochgeladenen CSS-Datei jedoch nicht funktioniert, stellen wir die beiden restlichen CSS-Anweisungen ins Seitentemplate (Abbildung 12).

<style>
.cczdhtmlmenuull1 .cczdhtmlmenuhandle {
  background-image: url("#WORKSPACE_IMAGES#down_dark_12x12.gif"); 
  top:  12px;
}
.cczdhtmlmenuull2 .cczdhtmlmenuhandle {
  background-image: url("#WORKSPACE_IMAGES#left_dark_12x12.gif"); 
  background-position: center;
  top: 0px;
}
</style>

CSS-Anweisungen mit Bilddateien und "#WORKSPACE_IMAGES#" im Seitentemplate

Abbildung 12: CSS-Anweisungen mit Bilddateien und "#WORKSPACE_IMAGES#" im Seitentemplate

Starten Sie die Anwendung nochmals. Nun sieht die Seite wie in Abbildung 13 aus ...

Anwendungsseite nach dem Einbau der CSS-Angaben

Abbildung 13: Anwendungsseite nach dem Einbau der CSS-Angaben

Das Pulldown-Menü nimmt Gestalt an. Nun ist nur noch die erste Ebene sichtbar - alle anderen Ebenen sind ausgeblendet - schließlich sollen sie erst bei Klick eingeblendet werden. Im Moment passiert bei einem Klick aber noch nichts - das ändert sich nun aber. Erzeugen Sie mit dem folgenden JavaScript-Code eine Datei namens PullDownMenu.js und laden Sie diese bei den Gemeinsame Komponenten und dort bei Statische Dateien als Workspace-Datei hoch (Abbildung 14).

  function addMouseOver() {
    $(".cczdhtmlmenuull1 .cczdhtmlmenuhandle").bind("click", function() {openmenunode(this);});
    $(".cczdhtmlmenuull2 li").bind("mouseover", function() {highlightnode(this);});
    $(".cczdhtmlmenuull2 li").bind("mouseout", function() {nohighlightnode(this);});
    $(document).bind("click", function(event) {
      if (!$(event.target).is(".cczdhtmlmenuull1 *")) {
        $(".cczdhtmlmenuull1 ").find("li").removeClass("open");
        $(".cczdhtmlmenuull1 ul").hide();
        $(".cczdhtmlmenuull1 ul").css("visibility","hidden");
      }  
    });
  }
  $(document).ready(function() {addMouseOver();} );

  function nohighlightnode(menunode) {
    $(menunode).removeClass("cczdhtmlselectedNode");
  }
  function highlightnode(menunode) {
    $(menunode).addClass("cczdhtmlselectedNode");
  }

  function openmenunode(menunode) {
    // Status feststellen 
    var isOpen = $(menunode).parent().hasClass("open"); 

    // Ausgehend von geklicktem Knoten - gesamtem Baum schließen
    $(menunode).parents("ul:first").find(".open").removeClass("open");
    $(menunode).parents("ul:first").find("ul").hide();
    $(menunode).parents("ul:first").find("ul").css("visibility","hidden");

    // Status des geklickten Knotens wiederherstellen
    if (isOpen) {
      $(menunode).parent().addClass("open"); 
    }
   
    // Wenn der geklickte Knoten offen ist ...
    if ($(menunode).parent().hasClass("open")) { 
      // dann schließen 
      $(menunode).parent().removeClass("open");
      $(menunode).parent().children("ul:first").hide();
      $(menunode).parent().children("ul:first").css("visibility","hidden");
    } else {
      $(menunode).parent().children("ul:first").show();
      $(menunode).parent().children("ul:first").css("visibility","visible");
      $(menunode).parent().children("ul:first").find("ul").hide();
      $(menunode).parent().children("ul:first").find("ul").css("visibility","hidden");
      $(menunode).parent().addClass("open");

      if ( $(menunode).parent().hasClass("cczdhtmlmenulil1") ) {
          // $(menunode).parent().children("ul:first").css("left", $(menunode).parent().position().left);
           $(menunode).parent().children("ul:first").css("left", 0);
          $(menunode).parent().children("ul:first").css("top",  $(menunode).parent().height());
      } else {
          $(menunode).parent().children("ul:first").css("left", $(menunode).parent().width());
          // $(menunode).parent().children("ul:first").css("top", ($(menunode).parent().position().top - $(menunode).parent().height()));
      }
    }
  }
Hochladen der JavaScript-Datei als "Statische Datei"

Abbildung 14: Hochladen der JavaScript-Datei als "Statische Datei"

Schließlich wird auch diese Datei wie folgt im Seitentemplate referenziert.

<script type="text/javascript" src="#WORKSPACE_IMAGES#PullDownMenu.js"></script>
Referenzieren des JavaScript-Code im Seitentemplate

Abbildung 15: Referenzieren des JavaScript-Code im Seitentemplate

Wenn Sie die Seite nun neu starten, sollte das Pulldown-Menü funktionieren - dank des JavaScript-Codes werden die Mausklicks nun abgefangen und die Untermenüs öffnen und schließen sich dementsprechend.

Das Pulldown-Menü funktioniert nun

Abbildung 16: Das Pulldown-Menü funktioniert nun

Seit APEX 4.1 können Listen auch auf Basis einer SQL-Abfrage generiert werden. Damit liegt es nahe, ein Pulldown-Menü für die Tabelle EMP zu erzeugen. Erzeugen Sie also in den Gemeinsamen Komponenten eine neue Liste - nehmen Sie aber eine dynamische Liste und hinterlegen Sie die folgende SQL-Abfrage.

select 
  level,
  ename || ' ('|| job || ')' ename, 
  null
from emp 
start with mgr is null
connect by mgr = prior empno

Wenn Sie dann eine neue Listen-Region erzeugen, darin die neue Liste auf die EMP-Tabelle auswählen und dann als Template das neue Template PullDownMenu wählen, sollte die Seite wie in Abbildung 17 aussehen.

Tabelle EMP als Pulldown-Menü

Abbildung 17: Tabelle EMP als Pulldown-Menü

Damit können Sie auch Ihre Anwendungen mit einem Pulldown-Menü ausstatten - eben wie im Application Builder. Durch Anpassen der CSS-Angaben lässt sich das Layout beliebig verändern. Probieren Sie es am besten gleich aus.

Zurück zur Community-Seite