Zum Inhalt springen

Einleitung

Je nach Einsatzzweck besitzen Formulare eine Menge Felder. Um den Nutzer kognitiv zu entlasten, kann man die Formularfelder in mehrere Schritte aufteilen. Ihr kennt das selbst aus dem Checkout-Prozess jedes Onlineshops: vom Warenkorb geht es zur Eingabe der Rechnungsadresse, dann folgt die Lieferadresse sowie die gewünschte Zahlungsart.

Aus technischer Sicht erleichtert der Einsatz mehrerer Formularseiten im TYPO3 Form Framework auch die Einrichtung optionaler Felder mit Variants (Conditions). So kann der gesamte Schritt mit der Lieferadresse entfallen, wenn sie mit der Rechnungsadresse übereinstimmt.

Greift man auf ein mehrseitiges Formular zurück, sollte man dem Nutzer jederzeit zeigen, an welcher Stelle im Eingabeprozess er aktuell steht.

Diese Fortschrittsanzeige kann ein simpler farbiger Balken sein, der den Verlauf prozentual darstellt, oder eine detailliertere Darstellung mit Titeln und ggf. Nummerierung der Schritte.

Um die Einrichtung eines solchen Indikators soll es heute gehen.

Ausgangslage

Unser Beispielformular stellt einen leicht vereinfachten Bestellvorgang dar. Im Formular sind vier Seiten definiert:

  1. Produktauswahl
  2. Rechnungsadresse
  3. Lieferadresse (optional)
  4. Übersicht

Der Formularschritt mit der Lieferadresse kann vom Nutzer übersprungen werden, wenn er auf der vorherigen Seite die Checkbox "Rechnungsadresse entspricht der Lieferadresse." aktiviert.

Die nachfolgenden Screenshots zeigen die vier Formularseiten als fertiges Ergebnis.

Zu diesem Tutorial gibt es einen zweiten Teil, in der die Einrichtung der stark individualisierten Übersichtsseite beschrieben ist.

Templating

Als Vorlage dienen die mit Bootstrap 5 kompatiblen Fluid-Templates aus dem Form Framework, die ihr unter EXT:form/Resources/Private/FrontendVersion2/ findet. Deren Verwendung muss mit der YAML-Option templateVariant: version2konfiguriert werden.

Bevor wir unsere neue Stepper-Navigation einrichten, müssen wir die bereits bestehende Formular-Navigation anpassen. Die Buttons für den Wechsel vor/zurück sind im Original unterschiedlich eingerichtet: in der Condition mit {form.previousPage} wird ein verstecktes Input-Feld für den Index-Wert der Seite und weiterer Attribute verwendet.

Dies wäre inkompatibel mit der Stepper-Navigation. Wahlweise ist dann die Funktion der Stepper-Navigation oder des Zurück-Buttons gestört.

Daher ergänzen wir in unserem Template-Override alle Attribute des Input-Feldes direkt am Button:

<nav class="{form.renderingOptions.formNavigation.navigationClassAttribute}" aria-label="{form.renderingOptions.formNavigation.navigationAriaLabelAttribute}">
    <f:if condition="{form.previousPage}">
        <f:form.button type="submit" property="__currentPage" value="{form.previousPage.index}" additionalAttributes="{respectSubmittedDataValue: false}" class="{form.renderingOptions.formNavigation.btnPreviousClassAttribute}" formnovalidate="formnovalidate">{formvh:translateElementProperty(element: form.currentPage, renderingOptionProperty: 'previousButtonLabel')}</f:form.button>
    </f:if>
    <f:if condition="{form.nextPage}">
        <f:then>
            <f:form.button property="__currentPage" value="{form.nextPage.index}" class="{form.renderingOptions.formNavigation.btnNextClassAttribute}">{formvh:translateElementProperty(element: form.currentPage, renderingOptionProperty: 'nextButtonLabel')}</f:form.button>
        </f:then>
        <f:else>
            <f:form.button property="__currentPage" value="{form.pages -> f:count()}" class="{form.renderingOptions.formNavigation.btnSubmitClassAttribute}">
                {formvh:translateElementProperty(element: form, renderingOptionProperty: 'submitButtonLabel')}
            </f:form.button>
        </f:else>
    </f:if>
</nav>

Templates/Form.html

Jetzt können wir im Formular-Template ein neues Partial für die Stepper-Navigation einbinden:

<formvh:renderRenderable renderable="{form}">
    <formvh:form
        object="{form}"
        action="{form.renderingOptions.controllerAction}"
        method="{form.renderingOptions.httpMethod}"
        id="{form.identifier}"
        section="{form.identifier}"
        enctype="{form.renderingOptions.httpEnctype}"
        addQueryString="{form.renderingOptions.addQueryString}"
        argumentsToBeExcludedFromQueryString="{form.renderingOptions.argumentsToBeExcludedFromQueryString}"
        additionalParams="{form.renderingOptions.additionalParams}"
        additionalAttributes="{formvh:translateElementProperty(element: form, property: 'fluidAdditionalAttributes')}"
    >
        <div class="mb-5">
            <f:render partial="Form/MultiStepNavigation" arguments="{form: form}"/>
        </div>
        <f:render partial="{form.currentPage.templateName}" arguments="{page: form.currentPage}" />
        <div class="{form.renderingOptions.formNavigation.navigationWrapperClassAttribute}">
            <f:render partial="Form/Navigation" arguments="{form: form}" />
        </div>
    </formvh:form>
</formvh:renderRenderable>

Stellt hier unbedingt sicher, dass euer Partial innerhalb des formvh:form ViewHelpers eingebunden ist. Sonst funktioniert eure Navigation nicht.

Partials/Form/MultiStepNavigation.html

Hier folgt jetzt die eigentliche Stepper-Navigation:

<nav>
    <ol class="c-stepper">
        <f:for each="{form.pages}" as="page" iteration="step">
            <f:if condition="{page.index} == {form.currentPage.index}">
                <f:then>
                    <f:comment><!-- Current page (not linked) --></f:comment>
                    <li class="c-stepper__item c-stepper__item--current">
                        <div class="c-stepper__circle">
                            <span class="c-stepper__label" aria-current="page">{formvh:translateElementProperty(element: page, property: 'label')}</span>
                        </div>
                    </li>
                </f:then>
                <f:else>
                    <f:if condition="{page.index} < {form.currentPage.index}">
                        <f:then>
                            <f:comment><!-- Previous pages --></f:comment>
                            <li class="c-stepper__item">
                                <f:form.button property="__currentPage" value="{page.index}"
                                               class="u-button-reset c-stepper__circle"
                                               additionalAttributes="{respectSubmittedDataValue: false}"
                                               formnovalidate="formnovalidate">
                                    <span class="c-stepper__label">{formvh:translateElementProperty(element: page, property: 'label')}</span>
                                </f:form.button>
                            </li>
                        </f:then>
                        <f:else>
                            <f:comment><!-- Upcoming pages --></f:comment>
                            <li class="c-stepper__item c-stepper__item--next">
                                <div class="c-stepper__circle">
                                    <span class="c-stepper__label">{formvh:translateElementProperty(element: page, property: 'label')}</span>
                                </div>
                            </li>
                        </f:else>
                    </f:if>
                </f:else>
            </f:if>
        </f:for>
    </ol>
</nav>

Das Styling der Stepper-Navigation ist unabhängig von Bootstrap. Nehmt das Stylesheet der Demo gerne als Vorlage für euer Projekt.

Funktionsweise der Stepper-Navigation

  • Vorherige, bereits ausgefüllte Seiten kann der Nutzer über die Navigation direkt aufrufen. Jeder gerenderte Button erhält den jeweiligen Index-Wert der dazugehörigen Seite.
  • Die aktuelle Seite wird nicht verlinkt, da zwecklos.
  • Nachfolgende Seiten werden ebenfalls nicht verlinkt – selbst wenn der Nutzer bereits das komplette Formular ausgefüllt hat und dann zu einem vorherigen Schritt zurückgekehrt ist. Der Grund dafür liegt in der notwendigen Evaluierung jedes Schritts durch das Form Framework.

Demo

Die TYPO3-Extension "form_multistep" liefert ein komplettes Demo-Setup mit. Neben dem Formular und den notwendigen Templates habe ich diesmal auch eine grundlegende TypoScript-Konfiguration mit PAGE-Objekt ergänzt. Ihr könnt das TypoScript der Extension einfach in einem neuen Seitenbaum einbinden und erhaltet direkt ein (einigermaßen) ansprechendes Design mit Bootstrap sowie eigenen Styles für die Stepper-Navigation.

Die Formular-Konfiguration wird bei Installation der Extension automatisch in TYPO3 registriert. Da alle Konfigurationen über einen neuen Formular-Prototyp gesetzt sind, beeinflusst das nicht eure ggf. bestehenden Formulare.

EXT:form_multistep auf GitHub

Getestet wurde die Extension mit TYPO3 v11 und v12.