Tab Control: Grundlagen
Zurück: Demo-Übersicht
1. Übersicht und Zweck
Status: Navigations-Form [Live] (widgets::tabs()); client-
seitiges Content-Panel-Umschalten ohne Reload ist [Live]
(Abschnitt 7).
tabs() rendert heute einen Navigations-Tab-Strip: jeder Tab ist ein
vollständiger Link auf eine andere Seite. Zielgruppe: Formulare/Ansichten,
die in mehrere Reiter mit jeweils eigenem Server-State aufgeteilt sind (z. B.
Table-Designer-Tabs „Spalten„/„Indizes“/„Constraints„).
2. Voraussetzungen
- DokuWiki mit aktiviertem Plugin
wkfluentui. helper_plugin_wkfluentui_widgetsgeladen überplugin_load('helper', 'wkfluentui_widgets').- Jeder Tab braucht eine eigene, per URL erreichbare Ziel-Ansicht (kein Client-Panel-Umschalten in der aktuellen Ausbaustufe).
3. Konzepte
Navigation, nicht Panel-Umschalten: jeder Klick lädt die Zielseite neu — das ist beabsichtigt, kein fehlendes Feature. Der Erstkonsument (Table-Designer-Reiter) hält je Tab eigenen Server-State (unterschiedliche Formulare je Reiter), ein Reload ist dort korrekt. Ein echtes, client-seitiges Panel-Umschalten ist als eigener Vertrag spezifiziert (Abschnitt 7), nicht Teil der Live-Grundform.
4. Erste Schritte
/** @var helper_plugin_wkfluentui_widgets $widgets */ $widgets = plugin_load('helper', 'wkfluentui_widgets'); echo $widgets->tabs([ ['key' => 'spalten', 'label' => 'Spalten', 'href' => wl($ID, ['tab' => 'spalten'])], ['key' => 'indizes', 'label' => 'Indizes', 'href' => wl($ID, ['tab' => 'indizes'])], ['key' => 'constraints', 'label' => 'Constraints', 'href' => wl($ID, ['tab' => 'constraints'])], ], 'spalten');
Falsch: einen key-Wert als zweiten Parameter übergeben, der zu
keinem Tab passt, in der Erwartung, dass „der erste Tab“ als Fallback aktiv
markiert wird. Kein Tab gilt dann als aktiv — keine Fehlermeldung, aber auch
keine automatische Auswahl.
5. Verwendung
| Einsatz | Muster |
|---|---|
| Formular-Reiter mit eigenem Server-State je Tab | tabs() direkt, $active aus dem aktuellen Request-Parameter ableiten |
| Häufigster Konsument | Vertical Grid als Panel-Inhalt je Tab (Table-Designer-Muster) |
6. API-Referenz
| Metadatum | Wert |
|---|---|
| Sprache | PHP |
| Namespace | helper_plugin_wkfluentui_widgets |
| Datei | lib/plugins/wkfluentui/helper/widgets.php |
| Sichtbarkeit | public |
| Stabilität | stabil (Tier 1), entstanden für den SQLite Table Designer |
public function tabs(array $tabs, $active): string
Zusammenfassung: Rendert einen Navigations-Tab-Strip aus Tab- Definitionen; der aktuell aktive Tab wird per Schlüssel-Vergleich markiert.
Rückgabewert: string — <ul class="wk-tabs">…</ul>. Leerer
String bei leerem $tabs.
7. Parameter, Optionen und Zustände
$tabs (je Eintrag)
| Schlüssel | Typ | Pflicht | Bedeutung |
|---|---|---|---|
key | string | Ja | eindeutiger Bezeichner, gegen $active verglichen |
label | string | Ja | Tab-Text (hsc()-escaped) |
href | string | Ja | Ziel-URL |
$active (zweiter Methodenparameter, string): key des aktuell
aktiven Tabs. String-Vergleich; kein Match markiert keinen Tab als aktiv.
Erzeugtes Markup
<ul class="wk-tabs"> → je Tab
<li class="wk-tabs__item"><a class="wk-tabs__link[ wk-tabs__link--active]">…</a></li>.
Fehlerverhalten
| Bedingung | Verhalten |
|---|---|
$tabs leer | Rückgabe leerer String, kein Fehler |
$active passt zu keinem key | kein Tab als aktiv markiert, kein Fehler |
doppelte key-Werte | keine Prüfung — beide Tabs würden bei passendem $active gleichzeitig als aktiv markiert; der Aufrufer ist für eindeutige Keys verantwortlich |
8. Content-Panel-Umschalten ohne Reload [Live]
Der echte „Tab Control„ im DevExpress-Sinn (ASPxPageControl):
mehrere vorgerenderte Content-Panels liegen alle im DOM, ein Klick
auf einen Tab-Kopf blendet nur das passende Panel ein (hidden-Attribut
auf den übrigen, scripts/tabs.js) — kein Server-Roundtrip.
tabs($tabs, $active, ['mode' => 'nav'|'panels'])(Defaultnav= unverändertes Navigations-Verhalten, byte-identisch zur Vorversion).- Bei
panels:$tabs-Einträge tragen zusätzlich'panelHtml' => string(vertrauenswürdiges HTML, wiecontrolHtmlbeipropertyGrid()); jeder Tab-Kopf rendert als<button type="button" role="tab">statt<a>— keinhrefmehr, da nichts navigiert. Der Container (.wk-tabcontrol) trägtrole="tablist"/role="tabpanel"-Verdrahtung mit deterministischen ID-Paaren;$opts['id']benennt den Control (Default: aus den Tab-Keys abgeleitet, kollisionsfrei bei mehreren Controls je Seite). - No-JS-Fallback bei
panels: alle Panels sind gleichzeitig sichtbar untereinander, mit dem jeweiligen Tab-Titel als<h4>davor — erst die JS-Schicht (js-enabled-Klasse) blendet inaktive Panels und die Doppel-Titel aus; ohne JavaScript ist kein Inhalt unerreichbar.
echo $widgets->tabs([ ['key' => 'spalten', 'label' => 'Spalten', 'panelHtml' => $spaltenHtml], ['key' => 'indizes', 'label' => 'Indizes', 'panelHtml' => $indizesHtml], ], 'spalten', ['mode' => 'panels']);
9. Einschränkungen und Randfälle
- Tab-Overflow („Mehr…“-Menü bei schmalem Container) — Details: Adaptivity.
panelHtmlwird imnav-Modus ignoriert;hrefimpanels-Modus ebenso.
10. Accessibility und Kompatibilität
- Vollständiger Überblick: Accessibility (konsolidiert, nicht hier dupliziert).
- Als reine Link-Navigation trägt der Tab-Strip bewusst kein
role=„tablist“— diese Rolle wäre für reine Navigation nicht korrekt und würde Screenreadern fälschlich ein Panel-Umschalt-Widget signalisieren. - Ohne JavaScript vollständig bedienbar (native Link-Semantik).
11. Troubleshooting
Symptom: kein Tab erscheint als aktiv markiert.
Ursache: der übergebene $active-Wert stimmt mit keinem key in
$tabs überein (Tippfehler oder falscher Request-Parameter-Name).
Lösung: $active gegen die tatsächlichen key-Werte in $tabs
prüfen.
12. Verwandte Themen
- Demo-Übersicht — alle Kategorien
- FluentUI: Öffentliche Helper-API — vollständiger Methodenvertrag
- FluentUI: Vertical Grid (Property Grid) — Demo-Übersicht — häufigster Konsument