Antipattern: Het Droste effect (deel 1)

An article, posted about 15 years ago filed in development, software, AntiPattern, antipattern html jsf simplicity & notforhomepage.

 Een plaatje, waarin het plaatje zelf zit, waarin hetzelfde plaatje weer zit… je kent het wel: het Droste effect. Vaak kom je hetzelfde tegen in code. Om onszelf scherp te houden, voorbeelden uit de praktijk (die wij natuurlijk niet hanteren ;) ). Vandaag opmaak van webpagina’s. Het zijn stukjes code uit zgn. JSPX pagina’s, een opmaak techniek die gebruikelijk is binnen Java Server Faces. Vandaag beginnen we simpel: <h:outputPanel id="headerAdditionalProducts" layout="block" styleClass="header"> <h:outputText value="#{translate.HST.lbl_AdditionalProducts}" /> </h:outputPanel>

Wat vinden we hier opnieuw uit? Wel de extreemsten onder ons zullen zeggen: ASCII (dit biedt immers ook ondersteuning voor tekst), maar goed, XML heeft toch wel degelijk voordelen, vooral met betrekking tot leesbaarheid, hoewel het bovenstaande daar nou weer geen goed voorbeeld van is. En dat is dus precies de aanleiding voor deze post.

Waar ik op doel is de classe definitie die in het genoemde voorbeeld wordt gebruikt en verwijst naar een functie als ‘header’, een kopje. Verder zien we dat hier gebruik wordt gemaakt van een zgn. <h:outputPanel /> die waar niets bijzonders wordt gedaan. Een <h:outputPanel /> is een container die al dan niet gerenderd kan worden als een <div /> danwel als een <span />. Het typen van outputpanel is overkill… het kan korter. Laten we deze laatste constatering eerst aanpakken. De code wordt gerenderd als een <div /> (layout=”block”), dus: <div id="headerAdditionalProducts" class="header"> <h:outputText value="#{translate.HST.lbl_AdditionalProducts}" /> </div>

En aangezien het een header is, laten we dan ook gewoon een header element gebruiken: <h1 id="headerAdditionalProducts"> <h:outputText value="#{translate.HST.lbl_AdditionalProducts}" /> </h1>

Voordelen: iedereen weet weer waarover het gaat, wat het belang is van deze inhoud zonder het (style)Classe attribuut te lezen. Met een beetje geluk (daar leek het wel op in de context waar ik mijn voorbeeld tegen kwam) is het ‘id’-attribuut ook overbodig… maar goed, dat kan z’n nut hebben bij het refereren aan dit blok (als een anchor binnen de pagina).

De echte aanleiding van deze post was echter een groter probleem: tabulaire data werd vormgegeven in een wirwar van geneste <outputpanels>’s zonder enige duidelijke betekenis, noch groepering. Ik wilde er aan gaan werken, maar het koste mij bijna een halve dag om te doorgronden hoe het in elkaar zat. Erger dan je zelf herhalen (DRY (Don’t Repeat Yourself)), is repeat others who are smarter than you (waarmee ik niet doel op mijzelf, maar op groeperingen die standaard oplossingen bedenken). Tuurlijk, je kunt dingen abstraheren, simpeler maken… maar ingewikkelder bovenop een bestaande?

Een oplossing van tabellen namaken met divjes is zoiets. Om een tabel te maken met <div/>jes die niet een standaard structuur hebben vereist veel gesleutel met CSS. Het kan wel, en het is in sommige gevallen ook beter (wanneer de data niet tabulair is, en tables enkel worden gebruikt als een ‘hack’ om het uiterlijk goed te krijgen (hierover is veel te vinden op google)), maar het is echt zinloos om exact tabellen na te maken wanneer de inhoud tabulair is. Ik vond: <a4j:outputPanel id="HeaderRow" layout="block" styleClass="data_row header"> <a4j:outputPanel layout="block" styleClass="kolomAbreedte"> Kop A </a4j:outputPanel> <a4j:outputPanel layout="block" styleClass="kolomBbreedte"> Kop B </a4j:outputPanel> <a4j:outputPanel layout="block" styleClass="kolomCbreedte"> Kop C </a4j:outputPanel> </a4j:outputPanel> <a4j:repeat value="#{datawaarweoveritereren}" var="waarde"> <a4j:outputPanel layout="block"> <a4j:outputPanel layout="block" styleClass="kolomAbreedte"> <h:outputText value="#{waarde}" /> </a4j:outputPanel> <a4j:outputPanel layout="block" styleClass="kolomBbreedte"> <h:outputText value="#{waarde}" /> </a4j:outputPanel> <a4j:outputPanel layout="block" styleClass="kolomCbreedte"> <h:outputText value="#{waarde}" /> </a4j:outputPanel> </a4j:outputPanel> </a4j>

Een al versimpeld stukje code (ik sla de stap om van jspx specifieke tags naar divs uit te leggen verder over): <div class="tabel"> <div class="rij kop"> <div class="cell" class="kolomAbreedte">Kop A</div> <div class="cell" class="kolomBbreedte">Kop B</div> <div class="cell" class="kolomCbreedte">Kop C</div> </div> <a4j:repeat value="#{datawaarweoveritereren}" var="waarde"> <div> <div class="cell" class="kolomAbreedte"> <h:outputText value="#{waarde}" /> </div> <div class="cell" class="kolomBbreedte"> <h:outputText value="#{waarde}" /> </div> <div class="cell" class="kolomCbreedte"> <h:outputText value="#{waarde}" /> </div> </a4j:repeat> </div> </div>

Dit kan weergegeven worden als een drie koloms tabel… maar het vereist ook de nodige css. En garanderen dat kolommen recht zijn is lastig. Wel mogelijk, maar lastig.

Alternatief kun je zeggen: <tabel> <thead> <tr> <td>Kop A</td> <td>Kop B</td> <td>Kop C</td> </tr> </thead> <a4j:repeat value="#{datawaarweoveritereren}" var="waarde"> <tr> <td><h:outputText value="#{waarde}" /></td> <td><h:outputText value="#{waarde}" /></td> <td><h:outputText value="#{waarde}" /></td> </tr> </a4j:repeat> </tabel>

En wat we zonet wilden, krijgen we gratis. En het blijft tenminste overzichtelijk, vooral wanneer je html al weet te lezen. In JSF kan het zinvol zijn om de jsf tags te gebruiken. Maar <h:outputPanel/>’s zijn echt niet de enige tags. Er zijn in jsf b.v. <h:datatables/>, die ons toestaan om over data te itereren (iets wat niet kan met de standaard html. Een alternatief kan b.v. zijn: <h:dataTable value="#{datawaarweoveritereren}" var="waarde"> <h:column> <f:facet name="header">Kop A</f:facet> <h:outputText value="#{waarde} </h:column> <h:column> <f:facet name="header">Kop B</f:facet> <h:outputText value="#{waarde} </h:column> <h:column> <f:facet name="header">Kop C</f:facet> <h:outputText value="#{waarde} </h:column> </h:dataTable>

Ook niet heel overzichtelijk? Wel, hier krijgen veel krachtigere opties voor terug. Maar omdat uit te leggen… daar zijn betere handleidingen voor :) Mogelijk is JSF wel helemaal niet de meest gewenste technologie voor hetgeen wat je gedaan probeert te krijgen. Moraal van het verhaal: eer je aan iets begint, verdiep je er in. Google vervangt (helaas?) nog niet het verstand hebben van dingen.                      

Deze post verscheen eerder op The Bean Blog

Op de hoogte blijven?

Maandelijks maak ik een selectie artikelen en zorg ik voor wat extra context bij de meer technische stukken. Schrijf je hieronder in:

Mailfrequentie = 1x per maand. Je privacy wordt serieus genomen: de mailinglijst bestaat alleen op onze servers.