Skip to content

Introduction

If you have ever added a summary page to a form in TYPO3, you know the default styling of this page: all form data is just listed one after the other. Labels stay on the left, field values on the right (if using Bootstrap).

If you have a rather large form, this simple listing will definitely be too confusing. Labels could even be repeated here, e.g. for the two "city" fields of a given billing and shipping address.

Therefore, we will focus in this article on the customization of the summary page.

Starting point

This tutorial is the follow-up to the article about multi-step forms. In the first tutorial, I added a stepper navigation to a multi-page form.

Our example form consists of four pages:

  1. Product selection
  2. Billing address
  3. Shipping address (optional)
  4. Summary

The form step containing the shipping address can be skipped by the user by checking the checkbox "Use billing address as shipping address." on the preceding page.

The code snippets on this page show only the most important parts. You can find the full code in the demo extension on GitHub.

Rendering of all fields, grouped by form pages

By default, the Summary Page uses the ViewHelper formvh:renderAllFormValues to cycle through all form elements. Inside it, the form elements are then rendered. The value passed to the ViewHelper for this is {page.rootForm}.

With only a few adjustments, we can group the fields by form pages and add the label of each page as a heading:

<f:for each="{page.rootForm.pages}" as="step">
    <f:if condition="{step.type} != 'SummaryPage'">
        <h2>{formvh:translateElementProperty(element: step, property: 'label')}</h2>
    </f:if>

    <formvh:renderAllFormValues renderable="{page.rootForm.pages.{step.index}}" as="formValue">
        <!-- Rendering of the fields (identical to the original template) -->
    </formvh:renderAllFormValues>
</f:for>

This change already provides a more structured result. But to make the summary truly user-friendly, more effort is required.

Optimized rendering of single field values

Both address blocks become much more readable if you omit the field labels and display the entered data in human-readable form:

John Doe
Great Russell St
WC1B 3DG London
United Kingdom

To achieve this, it would be convenient to be able to access single form values. The second ViewHelper formvh:renderFormValue, which is available from TYPO3 v10, allows exactly that. You pass it the desired form element with the identifier in the form definition.

For the billing address, it may look like this:

<p>
    <formvh:renderFormValue renderable="{page.rootForm.elements.firstName--billing}" as="formValue">
        {formValue.processedValue}
    </formvh:renderFormValue>
    <formvh:renderFormValue renderable="{page.rootForm.elements.lastName--billing}" as="formValue">
        {formValue.processedValue}<br>
    </formvh:renderFormValue>
    <formvh:renderFormValue renderable="{page.rootForm.elements.address--billing}" as="formValue">
        {formValue.processedValue}<br>
    </formvh:renderFormValue>
    <formvh:renderFormValue renderable="{page.rootForm.elements.postalCode--billing}" as="formValue">
        {formValue.processedValue}
    </formvh:renderFormValue>
    <formvh:renderFormValue renderable="{page.rootForm.elements.city--billing}" as="formValue">
        {formValue.processedValue}<br>
    </formvh:renderFormValue>
    <formvh:renderFormValue renderable="{page.rootForm.elements.country--billing}" as="formValue">
        {formValue.processedValue}
    </formvh:renderFormValue>
</p>

Adding conditions

In our example form, the user can check a checkbox to skip the shipping address. On the summary page, we can take this into account:

<formvh:renderFormValue renderable="{page.rootForm.elements.address-is-identical}" as="formValue">
    <f:if condition="{formValue.value}">
        <f:then>
            <p>{formvh:translateElementProperty(element: formValue.element, property: 'label')}</p>
        </f:then>
        <f:else>
            <p>
                <formvh:renderFormValue renderable="{page.rootForm.elements.firstName--shipping}" as="formValue">
                    {formValue.processedValue}
                </formvh:renderFormValue>
                <!-- Rendering of the remaining fields of the shipping address (see demo) -->
            </p>
        </f:else>
    </f:if>
</formvh:renderFormValue>

If the checkbox is active, its label "Use billing address as shipping address." is displayed. Otherwise, the field data of the billing address will be rendered.

Iterate through subparts of a form, e.g. fieldsets

As you can see, rendering fields one by one can become a tedious task. But we don't need to output all fields this way.

Here, we iterate through all the form fields that are part of the "Products" fieldset:

<formvh:renderAllFormValues renderable="{page.rootForm.elements.fieldsetProducts}" as="formValue">
    <!-- Rendering of the products fields (see demo) -->
</formvh:renderAllFormValues>

This would allow you to create a set of order forms where only the products differ. The once-individualized SummaryPage template can then be used for all these forms.

Buttons to edit entered data in a form step

On the summary page, we have now arranged the entered form data in a meaningful way.

Thanks to the setup of the stepper navigation in the first tutorial, we already know how to navigate back to a specific form step.

Therefore, we can now add an "edit" button for each block of data on the summary page, which allows to open the corresponding form page.

In the SummaryPage template, we can add a button to each relevant text block. Here, we set the index value of the form step manually. If you iterate through the individual pages in your form (as shown above), you can access and use their corresponding index values with value="{page.index}".

<f:form.button property="__currentPage" value="2"
               class="btn btn-primary btn-sm"
               additionalAttributes="{respectSubmittedDataValue: false}"
               formnovalidate="formnovalidate">
    {f:translate(key: 'LLL:EXT:form_multistep/Resources/Private/Language/form.xlf:btn.edit.label')}
</f:form.button>

By the way, we do not need a condition whether the form page with index value 2 is currently enabled! If the page with the shipping address was skipped in our example form (means this page is disabled), a click on the button for this page will open the preceding, active page instead.
If your form is more complex, the button's value could of course be set with a proper condition.

Demo

The TYPO3 extension "form_multistep" provides a complete demo setup. Besides the form and the necessary templates, I've also added a basic TypoScript configuration with PAGE object this time. You can simply include the TypoScript of the extension in a new page tree to get a (somewhat) appealing design with Bootstrap, as well as custom styles for the stepper navigation.

The form configuration is automatically registered in TYPO3 when you install the extension. Since all configurations are defined within a new form prototype, this should not interfere with your existing forms.

EXT:form_multistep on GitHub

The extension was tested with TYPO3 v11 and v12.