5. April 2013

Mehrspaltige, responsive Formulare mit powermail 2, Fluid und Twitter Bootstrap

In den letzten Jahren ist die Nutzung des mobilen Internets in Deutschland von 13% im Jahre 2008 rasant auf über 50% im letzten Jahr angestiegen (vgl. Accenture). Dabei geht es schon lange nicht mehr nur um das Chatten mit Freunden oder Nutzen von Apps, sondern immer mehr auch darum, reguläre Webseiten über das Smartphone oder Tablett aufzurufen (vgl. Vivaki). Umso wichtiger ist es, Nutzern mobiler Geräte ein optimales Erlebnis zu bieten.

Die Anpassung einer Webseite an verschiedene Auflösungen mittels CSS (das sogenannte "responsive Webdesign") ist ein Schritt, Inhalte für unterschiedlichste Geräte bestmöglich darzustellen. Zahlreiche CSS-Frameworks (beispielsweiseTwitter Bootstrap oder Foundation von Zurb) vereinfachen diesen Prozess, sind jedoch in Verbindung mit von Content-Management-Systemen generierten Formularen häufig nur schwer zu vereinbaren.

Der folgende Artikel zeigt eine Möglichkeit, mehrspaltige und responsive Formulare - generiert mit der TYPO3-Extensionpowermail - auf Basis von Twitter Bootstrap umzusetzen.

Voraussetzungen: Das ist nötig

Neben dem CSS-Framework Twitter Bootstrap (inklusive der responsiven Version) wird die TYPO3-Extension "powermail" ab Version 2.0 benötigt. Zudem wird Zugriff auf die von powermail genutzen Fluid-Templates vorausgesetzt.

Das Fundament: Erstellung der Grundstruktur mit Fluid

Zunächst soll ein zweispaltiges Layout basierend auf dem Grid-System von Twitter Bootstrap umgesetzt werden:

Dieses Kontaktformular ist zweispaltig und responsiv mit Twitter Bootstrap umgesetzt.

Um unabhängig von der Größe umgebender Spalten und Containern arbeiten zu können, wird anstatt der standardmäßigen Klasse für einzelne Reihen '.row' - die mit festen, pixelpasierten Breiten arbeitet - die flexiblere Klasse '.row-fluid' genutzt.

Um eine solche "Reihe" um alle Elemente eines Formulars zu rendern, kann das Fluid-Template des powermail-Formulars etwa wie folgt angepasst werden:

Templates/Forms/Form.html:

(…) 
<f:for each="{form.pages}" as="page">
	<fieldset class="powermail_fieldset powermail_fieldset_{page.uid} {page.css}">
		<legend class="powermail_legend">{page.title}</legend>
		<!-- hier beginnt der Inhaltsbereich des Formulars -->
		<div class="row-fluid">
			<!-- die einzelnen Elemente werden innerhalb einer '.row-fluid' gerendert -->
			<f:for each="{page.fields}" as="field" iteration="iteration">
				<f:render partial="Forms/{vh:String.Upper(string: '{field.type}')}" arguments="{field: field}" />
			</f:for>
		</div>
	</fieldset>
</f:for>
(…)

Eine '.row-fluid' bietet grundsätzlich Platz für 12 Spalten (ausgezeichnet durch die Klassen '.span1' bis '.span12'). Für ein zweispaltiges Layout sind folglich neben einer '.row-fluid' mindestens zwei Elemente der Klasse '.span6' nötig. Da powermail standardmäßig bereits ein <div> um die einzelnen Formularelemente generiert, muss diesem lediglich noch die entsprechende span-Klasse ('.span6') mitgegeben werden:

Partials/Forms/Input.html:

<div id="powermail_fieldwrap_{field.uid}" class="... span6">
	<!-- das Input-Element mit Label etc. -->
</div>

Problem: Zu viele Spalten in einer Reihe

Auf diese Weise wird das Formular bereits responsiv gerendert: Bei größeren Auflösungen (über 768px Breite) werden die '.span6' zweispaltig nebeneinander, für Smartphones und kleinere Tabletts hingegen untereinander dargestellt.

Dies funktioniert jedoch nur für die ersten beiden Elemente der '.row-fluid'. Alle weiteren Elemente werden einspaltig gerendert und haben einen zu großen Abstand auf der linken Seite:

Um dieses Problem zu lösen gibt es grundsätzlich zwei Möglichkeiten:

  • Im TYPO3-Backend werden die jeweils ersten Input-Elemente einer neuen Spalte mit einer speziellen Klasse ausgezeichnet, die das Element auf 'margin-left: 0px;' setzt. Dies kann über die TSconfig Konfiguration von powermail erreicht werden.
  • Beim Rendern der Elemente in Fluid wird mitgezählt und für jeweils zwei Elemente eine neue '.row-fluid' erstellt.

Die zweite Lösung erzeugt zwar mehr Markup (da mehrere '.row-fluid' erzeugt werden), erfordert jedoch keinen zusätzlichen Aufwand des Nutzers beim Erstellen und Bearbeiten von Formularen.

Um beim Rendern der Formularelemente mitzuzählen und entsprechend neue '.row-fluid'-Elemente zu erzeugen, muss das oben gezeigte Fluid-Template wie folgt angepasst werden:

Templates/Forms/Form.html

(…) 
<f:for each="{form.pages}" as="page">
	<fieldset class="powermail_fieldset powermail_fieldset_{page.uid} {page.css}">
		<legend class="powermail_legend">{page.title}</legend>
		<!-- beim Rendern der Formularelemente mitzählen und den Wert als 'iteration' bereitstellen -->
		<f:for each="{page.fields}" as="field" iteration="iteration">
			<!-- für ungerade Elemente wird eine neue '.row-fluid' geöffnet …-->
			<f:if condition="{iteration.isOdd}">
				<div class="row-fluid">
			</f:if>
			<f:render partial="Forms/{vh:String.Upper(string: '{field.type}')}" arguments="{field: field}" />
			<!-- … und bei ungeraden oder dem letzten Element wieder geschlossen -->
			<f:if condition="{iteration.isLast}">
				<f:then>
					</div>
				</f:then>
				<f:else>
					<f:if condition="{iteration.isEven}">
						</div>
					</f:if>
				</f:else>
			</f:if>
		</f:for>
	</fieldset>
</f:for>
(…)

Durch den 'iteration'-Parameter einer <f:for>-Schleife ist das Abzählen von jeweils zwei Elementen leicht: <f:if condition="{iteration.isOdd}"> liefert true für das jeweils erste Element, <f:if condition="{iteration.isEven}"> für das jeweils zweite Element einer Zeile. Wichtig dabei ist, nicht nur auf gerade und ungerade Zahlen zu prüfen, sondern auch sicherzustellen, dass nach dem letzten Formularelement immer die '.row-fluid' geschlossen wird. Dies ist mittels iteration.isLast ebenfalls leicht umzusetzen.

Hinweis: Da iteration.isEven und iteration.isOdd auf iteration.cycle (startet Zählung bei 1) und nicht auf iteration.index (startet Zählung bei 0) zugreifen, liefert iteration.isOdd und nicht iteration.isEven das erste Element!

Bis ins Detail: Letzte Schönheitskorrekturen

Damit die einzelnen <input>-Elemente immer die volle Breite ihrer Spalte einnehmen, können sie mit der Bootstrap-Klasse '.input-block-level' ausgezeichnet werden. Dies ist in den Fluid-Templates der einzelnen Elemente (unter Partials/Forms/ ) möglich.

Um bei ungerader Anzahl von <input>-Elementen (oder bei einzelnen Elementen, wie einer Checkbox für die AGB) das letzte Element mit voller Breite darzustellen, hilft die Verknüpfung der beiden CSS-Pseudo-Klassen :first-child und :last-child:

/* wenn das .span6 gleichzeitig erstes und letztes Kindelement ist, ist es das einzige Element in der .row-fluid */
.row-fluid .span6:first-child:last-child {
    width: 100%; /* also soll es die volle Breite einnehmen… */	
    margin: 0; /* …und nicht eingerückt sein */
}

Nicht nur für zweispaltige Formulare

In diesem Beispiel wurde lediglich auf die Erstellung eines zweispaltigen Layouts eingegangen. Der Prozess lässt sich jedoch leicht auf andere Spaltenzahlen übertragen: Im Fluid-Template des Formulars muss lediglich mitgezählt werden und nach der gewünschten Anzahl an Elementen eine neue '.row-fluid' geöffnet werden (hier kann <f:cycle> hilfreich werden).

Die einzelnen Elemente werden dann nicht mit '.span6', sondern mit '.span4' (für dreispaltige Layouts) bzw. '.span3' (für vierspaltige Layouts) ausgezeichnet.

Modern und responsiv mit TYPO3

Insgesamt lassen sich dank der weitreichenden Konfigurationsmöglichkeiten von TYPO3 - gerade in Verbindung mit Fluid - moderne und ansprechende Formulare erstellen, die für den Nutzer der Webseite dennoch komfortabel zu verwalten sind.

5. April 2013

Severin Glöckle