938 lines
35 KiB
ReStructuredText
938 lines
35 KiB
ReStructuredText
.. include:: /Includes.rst.txt
|
||
|
||
|
||
.. _concepts-frontendrendering:
|
||
|
||
==================
|
||
Frontend rendering
|
||
==================
|
||
|
||
.. _concepts-frontendrendering-templates:
|
||
|
||
Templates
|
||
=========
|
||
|
||
Fluid templates in the form framework are based on `Bootstrap`_.
|
||
|
||
.. _Bootstrap: https://getbootstrap.com/
|
||
|
||
.. _concepts-frontendrendering-templates-customtemplates:
|
||
|
||
Custom templates
|
||
----------------
|
||
|
||
In order to use your own Fluid templates for frontend forms,
|
||
register your own template paths via YAML in the form configuration
|
||
(here under the default ``standard`` prototype).
|
||
|
||
.. code-block:: yaml
|
||
|
||
prototypes:
|
||
standard:
|
||
formElementsDefinition:
|
||
Form:
|
||
renderingOptions:
|
||
templateRootPaths:
|
||
100: 'EXT:my_site_package/Resources/Private/Frontend/Templates/'
|
||
partialRootPaths:
|
||
100: 'EXT:my_site_package/Resources/Private/Frontend/Partials/'
|
||
layoutRootPaths:
|
||
100: 'EXT:my_site_package/Resources/Private/Frontend/Layouts/'
|
||
|
||
If your `form definition` then references the `standard` prototype, the form
|
||
framework will look for Fluid templates in
|
||
:directory:`EXT:my_site_package/Resources/Private/Frontend/[*]`.
|
||
|
||
The `Form` element is the 'main' element. The framework will look for
|
||
:file:`Form.html` in :directory:`templateRootPaths`. For all other elements,
|
||
it will look in :directory:`partialRootPaths`. A partial has the same name
|
||
as the `formElementTypeIdentifier` property, for example,
|
||
a `Text` template will be in a partial named :file:`Text.html` in
|
||
:directory:`partialRootPaths`.
|
||
|
||
|
||
.. _concepts-frontendrendering-templates-singlevalues:
|
||
|
||
Form element values in finisher templates
|
||
-----------------------------------------
|
||
|
||
Use the :php:`RenderFormValueViewHelper` to access form element values in your
|
||
finisher templates. This ViewHelper accepts a single form
|
||
element and renders it. The following example shows the :php:`RenderFormValueViewHelper`
|
||
being called with two parameters (`renderable` and `as`) to output the value of a
|
||
`message` field. The value :fluid:`{formValue.processedValue}` can then
|
||
be manipulated with Fluid, styled, etc.
|
||
|
||
.. code-block:: html
|
||
|
||
<formvh:renderFormValue renderable="{form.formDefinition.elements.message}" as="formValue">
|
||
{formValue.processedValue}
|
||
</formvh:renderFormValue>
|
||
|
||
Names of your form elements can be found in your form definition (in your
|
||
individual YAML files or in the :guilabel:`System > Configuration` module if you
|
||
have the lowlevel extension installed). Or use the debug ViewHelper in Fluid to
|
||
list all the form elements.
|
||
|
||
.. code-block:: html
|
||
|
||
<f:debug>{page.rootForm.elements}</f:debug>
|
||
|
||
|
||
.. _concepts-frontendrendering-translation:
|
||
|
||
Translation
|
||
===========
|
||
|
||
.. _concepts-frontendrendering-translation-formdefinition:
|
||
|
||
Translate form definition
|
||
-------------------------
|
||
|
||
Translation of `form definitions` works differently to the usual translation
|
||
of the backend. Currently, there is no graphical user interface
|
||
for this translation process.
|
||
|
||
If `form definition` properties were translated in the same way as the rest of the backend,
|
||
a backend editor using the `form editor` to edit a form they would see long
|
||
unwieldy translation keys. In order to avoid this, form element *properties* are translated
|
||
instead of their values. The form framework does not look for translation keys
|
||
in a translation file. Instead, the system searches for translations
|
||
of the form element properties independent of their property values. The
|
||
property values are ignored if an entry is found in a
|
||
translation file. The form element property values are overridden by the
|
||
translated values.
|
||
|
||
This approach is a compromise between two scenarios: creating forms using the `form editor`
|
||
or creating `form definitions` (which could later be edited in the
|
||
`form editor`). An editor can create forms just using the `form editor` where
|
||
form element property values are displayed in the default language. An integrator
|
||
can provide additional language files which translate the form depending on the
|
||
prototype.
|
||
|
||
Add additional translation files to the form configuration as follows:
|
||
|
||
.. code-block:: yaml
|
||
|
||
prototypes:
|
||
standard:
|
||
formElementsDefinition:
|
||
Form:
|
||
renderingOptions:
|
||
translation:
|
||
translationFiles:
|
||
# custom translation file
|
||
20: 'EXT:my_site_package/Resources/Private/Language/Form/locallang.xlf'
|
||
|
||
The translationFiles array is processed from the highest key to the lowest, i.e. your
|
||
translation file with key `20` is processed before translation files with key
|
||
'10' in EXT:form. If no key is found in the translation files, a
|
||
property value will be displayed unmodified.
|
||
|
||
The following properties can be translated:
|
||
|
||
* label
|
||
* defaultValue (scalar values only; array values, e.g. for :yaml:`MultiCheckbox`, are not translated)
|
||
* properties.[*]
|
||
* properties.options.[*]
|
||
* properties.fluidAdditionalAttributes.[*]
|
||
* renderingOptions.[*]
|
||
|
||
The translation keys are put together based on a specific pattern and there is a
|
||
order (fallback chain) for the translations that depends on translation scenarios.
|
||
These are the translation scenarios:
|
||
|
||
* translation of a form element property for a specific form (`formDefinitionIdentifier) and form
|
||
element (`ElementIdentifier`)
|
||
* translation of a form element property for a specific form element (`formElementIdentifier`) and
|
||
various forms
|
||
* translation of a form element property for an element type (`elementType`) and various
|
||
forms, e.g. the `Page` element
|
||
|
||
The look-up process searches for translation keys in all given translation
|
||
files based on the following order (the same order as the translation scenarios above):
|
||
|
||
* `<formDefinitionIdentifier>.element.<elementIdentifier>.properties.<propertyName>`
|
||
* `element.<formElementIdentifier>.properties.<propertyName>`
|
||
* `element.<elementType>.properties.<propertyName>`
|
||
|
||
Translation of options (`properties.options`) in form elements, like the
|
||
`Select` element, have the following look-up order:
|
||
|
||
* `<formDefinitionIdentifier>.element.<elementIdentifier>.properties.options.<propertyValue>`
|
||
* `element.<elementIdentifier>.properties.options.<propertyValue>`
|
||
|
||
.. _concepts-frontendrendering-translation-formdefinition-example:
|
||
|
||
Example Form Definition
|
||
~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
.. code-block:: yaml
|
||
|
||
identifier: ApplicationForm
|
||
type: Form
|
||
prototypeName: standard
|
||
label: 'Application form'
|
||
|
||
renderables:
|
||
-
|
||
identifier: GeneralInformation
|
||
type: Page
|
||
label: 'General information'
|
||
|
||
renderables:
|
||
-
|
||
identifier: LastName
|
||
type: Text
|
||
label: 'Last name'
|
||
properties:
|
||
placeholder: 'Please enter your last name.'
|
||
defaultValue: ''
|
||
-
|
||
identifier: Software
|
||
type: MultiSelect
|
||
label: 'Known software'
|
||
properties:
|
||
options:
|
||
value1: TYPO3
|
||
value2: Neos
|
||
|
||
In order to translate the form element `LastName`, the process will look for the following
|
||
translation keys in the translation files:
|
||
|
||
* `ApplicationForm.element.LastName.properties.label`
|
||
(*<formDefinitionIdentifier>.element.<elementIdentifier>.properties.<propertyName>*)
|
||
* `element.LastName.properties.label`
|
||
(*element.<formElementIdentifier>.properties.<propertyName>*)
|
||
* `element.Text.properties.label`
|
||
(*element.<elementType>.properties.<propertyName>*)
|
||
|
||
If none of these keys exist, 'Last name' will be displayed.
|
||
|
||
The :yaml:`defaultValue` of `LastName` can be translated with the same fallback chain,
|
||
using ``properties.defaultValue`` as the property name:
|
||
|
||
* `ApplicationForm.element.LastName.properties.defaultValue`
|
||
* `element.LastName.properties.defaultValue`
|
||
* `element.Text.properties.defaultValue`
|
||
|
||
In order to translate the form element `Software`, the process will look for the following
|
||
translation keys in the translation files:
|
||
|
||
* `ApplicationForm.element.Software.properties.label`
|
||
(*<formDefinitionIdentifier>.element.<elementIdentifier>.properties.<propertyName>*)
|
||
* `element.Software.properties.label`
|
||
(*element.<formElementIdentifier>.properties.<propertyName>*)
|
||
* `element.MultiSelect.properties.label`
|
||
(*element.<elementType>.properties.<propertyName>*)
|
||
|
||
If none of the these keys exist, 'Known software' will be
|
||
displayed. The option properties lookup process is as the following:
|
||
|
||
* `ApplicationForm.element.Software.properties.options.value1`
|
||
(*<formDefinitionIdentifier>.element.<elementIdentifier>.properties.options.<propertyValue>*)
|
||
* `element.Software.properties.options.value1`
|
||
(*element.<elementIdentifier>.properties.options.<propertyValue>*)
|
||
* `ApplicationForm.element.Software.properties.options.value2`
|
||
(*<formDefinitionIdentifier>.element.<elementIdentifier>.properties.options.<propertyValue>*)
|
||
* `element.Software.properties.options.value2`
|
||
(*element.<elementIdentifier>.properties.options.<propertyValue>*)
|
||
|
||
If none of the these keys exist, 'TYPO3' will be displayed as
|
||
label for the first option and 'Neos' for the second option.
|
||
|
||
.. _concepts-frontendrendering-translation-validationerrors:
|
||
|
||
Translation of validation messages
|
||
----------------------------------
|
||
|
||
The translation of validation messages is similar to the translation of
|
||
`form definitions` abpve. The same translation files can be used. If the look-up
|
||
process does not find a key within the files, an Extbase message will be displayed.
|
||
EXT:form translates validators by default.
|
||
|
||
The same as for `form definitions`, the translation keys are put together based on a
|
||
specific pattern. There is also a fallback chain.
|
||
|
||
The following translation scenarios are possible:
|
||
|
||
* translation of validation messages for a specific validator of a specific
|
||
form element (`elementIdentifier`) and specific form (`formDefinitionIdentifier`)
|
||
* translation of validation messages for a specific validator of various
|
||
form elements within a specific form (`formDefinitionIdentifier`)
|
||
* translation of validation messages for a specific validator of a specific
|
||
form element (`elementIdentifier`) in various forms
|
||
* translation of validation messages for a specific validator in various
|
||
forms
|
||
|
||
In Extbase, validation messages are identified by numerical codes (UNIX
|
||
timestamps). Different codes can be used for the same validator. Read more about
|
||
:ref:`concrete validator configurations <prototypes.prototypeIdentifier.validatorsdefinition.validatoridentifier-concreteconfigurations>`.
|
||
|
||
The look-up process searches for translation keys in the translation
|
||
files in the following order (the same order as the translation scenarios above):
|
||
|
||
* `<formDefinitionIdentifier>.validation.error.<elementIdentifier>.<validationErrorCode>`
|
||
* `<formDefinitionIdentifier>.validation.error.<validationErrorCode>`
|
||
* `validation.error.<elementIdentifier>.<validationErrorCode>`
|
||
* `validation.error.<validationErrorCode>`
|
||
|
||
.. _concepts-frontendrendering-translation-validation-example:
|
||
|
||
Example Form Definition with Validator
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
.. code-block:: yaml
|
||
|
||
identifier: ContactForm
|
||
type: Form
|
||
prototypeName: standard
|
||
label: 'Contact us'
|
||
|
||
renderables:
|
||
-
|
||
identifier: Page1
|
||
type: Page
|
||
label: 'Page 1'
|
||
|
||
renderables:
|
||
-
|
||
identifier: LastName
|
||
type: Text
|
||
label: 'Last name'
|
||
properties:
|
||
fluidAdditionalAttributes:
|
||
required: required
|
||
validators:
|
||
-
|
||
identifier: NotEmpty
|
||
|
||
|
||
If a user submits this form without providing a last name, the `NotEmpty`
|
||
validator (at the bottom of the example above) fails and
|
||
sends 1221560910 as a `<validationErrorCode>`. The system looks through the
|
||
translation keys in the following order searching for the `NotEmpty` validator for form element `LastName`:
|
||
|
||
* ContactForm.validation.error.LastName.1221560910 (*<formDefinitionIdentifier>.validation.error.<elementIdentifier>.<validationErrorCode>*)
|
||
* ContactForm.validation.error.1221560910 (*<formDefinitionIdentifier>.validation.error.<validationErrorCode>*)
|
||
* validation.error.LastName.1221560910 (*validation.error.<elementIdentifier>.<validationErrorCode>*)
|
||
* validation.error.1221560910 (validation*.error.<validationErrorCode>*)
|
||
|
||
As mentioned above, if no translation key is available,
|
||
a default Extbase framework message is displayed.
|
||
|
||
.. _concepts-finishers-translation:
|
||
.. _concepts-frontendrendering-translation-finishers:
|
||
|
||
Translation of finisher options
|
||
-------------------------------
|
||
|
||
The translation of finisher options is similar to the translation of
|
||
`form definitions` above. The same translation files can be used. If the look-up
|
||
process does not find a key in the provided translation files, the property value
|
||
will be displayed unmodified.
|
||
|
||
The same as for `form definitions`, the translation keys are put together based on a
|
||
specific pattern. There is also a fallback chain.
|
||
|
||
The following translation scenarios are possible:
|
||
|
||
* translation of finisher options for a specific finisher (`finisherIdentifier`) of a specific form (`formDefinitionIdentifier` below)
|
||
* translation of finisher options for a specific finisher (`finisherIdentifier`) of various forms
|
||
|
||
The look-up process searches for translation keys in all the translation
|
||
files based on the following order (the same order as the translation scenarios above):
|
||
|
||
* `<formDefinitionIdentifier>.finisher.<finisherIdentifier>.<optionName>`
|
||
* `finisher.<finisherIdentifier>.<optionName>`
|
||
|
||
The translation order is as follows:
|
||
|
||
1. Default value from form definition
|
||
2. Overridden value from a FlexForm (if any)
|
||
3. Localized value provided by translation files (if any)
|
||
|
||
The :yaml:`translation.propertiesExcludedFromTranslation` option skips the
|
||
third step so that the translation resolves to a FlexForm value if one exists.
|
||
For an example see
|
||
`Skip translation of overridden form finisher options <https://docs.typo3.org/permalink/typo3/cms-form:concepts-finishers-confirmationfinisher-yaml-propertiesexcludedfromtranslation>`_.
|
||
|
||
.. _concepts-frontendrendering-translation-finishers-example:
|
||
|
||
Example Form Definition with Finisher
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
.. code-block:: yaml
|
||
|
||
identifier: ContactForm
|
||
type: Form
|
||
prototypeName: standard
|
||
label: 'Contact us'
|
||
|
||
finishers:
|
||
-
|
||
identifier: Confirmation
|
||
options:
|
||
message: 'Thank you for your inquiry.'
|
||
|
||
renderables:
|
||
...
|
||
|
||
The look-up process searches for the following translation keys for the
|
||
'Confirmation' finisher message option:
|
||
|
||
* `ContactForm.finisher.Confirmation.message` (*<formDefinitionIdentifier>.finisher.<finisherIdentifier>.<optionName>*)
|
||
* `finisher.Confirmation.message` (*finisher.<finisherIdentifier>.<optionName>*)
|
||
|
||
If no translation key exists, the message 'Thank you for your inquiry.' will
|
||
be displayed.
|
||
|
||
|
||
.. _concepts-frontendrendering-translation-arguments:
|
||
|
||
Form element translation arguments
|
||
==================================
|
||
|
||
Form element property translations and finisher option translations can have
|
||
placeholders to output translation arguments. Translations can be enriched with
|
||
variable values by passing arguments to form element properties. This
|
||
feature was introduced in :issue:`81363`.
|
||
|
||
.. _concepts-frontendrendering-translation-properties:
|
||
|
||
Form element properties
|
||
-----------------------
|
||
|
||
In the YAML form configuration you can add simple literal values:
|
||
|
||
.. code-block:: yaml
|
||
|
||
renderables:
|
||
- identifier: field-with-translation-arguments
|
||
type: Checkbox
|
||
label: This is a %s feature
|
||
renderingOptions:
|
||
translation:
|
||
translationFiles:
|
||
10: path/to/locallang.xlf
|
||
arguments:
|
||
label:
|
||
- useful
|
||
|
||
This will produce the label: `This is a useful feature`.
|
||
|
||
Alternatively, you can use :typoscript:`formDefinitionOverrides` in TypoScript to set
|
||
translation arguments. One use case is a checkbox for
|
||
user confirmation which links to further information. Here it makes sense to use
|
||
YAML hashes (key value pairs) instead of YAML lists so that sections have keys. This simplifies
|
||
references in TypoScript since named keys are easy to read and can easily be reordered. With lists and numeric
|
||
keys the TypoScript setup would also need to be updated in this case.
|
||
|
||
In the following form configuration example the list of :yaml:`renderables` has been replaced with
|
||
a hash of :yaml:`renderables`, and the field :yaml:`field-with-translation-arguments`
|
||
now has a named key :yaml:`fieldWithTranslationArguments`. This key can be anything
|
||
as long as it is unique at its level in the YAML - here just the :yaml:`identifier`
|
||
in another form:
|
||
|
||
.. code-block:: yaml
|
||
|
||
renderables:
|
||
fieldWithTranslationArguments:
|
||
identifier: field-with-translation-arguments
|
||
type: Checkbox
|
||
label: I agree to the <a href="%s">terms and conditions</a>
|
||
renderingOptions:
|
||
translation:
|
||
translationFiles:
|
||
10: path/to/locallang.xlf
|
||
|
||
If the label contains HTML markup - like in the above example - it must
|
||
be wrapped in `CDATA` tags in the :directory:`path/to/locallang.xlf` translation file,
|
||
to prevent analysis of character data by the parser. Also, the
|
||
label should be rendered using the :fluid:`<f:format.raw>`
|
||
ViewHelper in fluid templates, to prevent escaping of HTML tags:
|
||
|
||
.. code-block:: xml
|
||
|
||
<trans-unit id="<form-id>.element.field-with-translation-arguments.properties.label">
|
||
<source><![CDATA[I agree to the <a href="%s">terms and conditions</a>]]></source>
|
||
</trans-unit>
|
||
|
||
The TypoScript below can use the :typoscript:`fieldWithTranslationArguments` key to refer
|
||
to the field and adds a page URL as a translation argument for the link in the label:
|
||
|
||
.. code-block:: typoscript
|
||
|
||
plugin.tx_form {
|
||
settings {
|
||
formDefinitionOverrides {
|
||
<form-id> {
|
||
renderables {
|
||
0 {
|
||
# Page
|
||
renderables {
|
||
fieldWithTranslationArguments {
|
||
renderingOptions {
|
||
translation {
|
||
arguments {
|
||
label {
|
||
0 = TEXT
|
||
0.typolink {
|
||
# Terms and conditions page, could be
|
||
# set also via TypoScript constants
|
||
parameter = 42
|
||
returnLast = url
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
The :yaml:`Page` element of the form definition is not registered with a named key so a numeric
|
||
key :yaml:`0` is used which, as mentioned above, is prone to errors when more pages are added
|
||
or reordered.
|
||
|
||
.. important::
|
||
|
||
There must be at least one translation file with a translation for the
|
||
form element property. Arguments are not inserted into default
|
||
values in a form definition.
|
||
|
||
Finishers
|
||
---------
|
||
|
||
The same mechanism (YAML, YAML + TypoScript) works for finisher options:
|
||
|
||
.. code-block:: yaml
|
||
|
||
finishers:
|
||
finisherWithTranslationArguments:
|
||
identifier: EmailToReceiver
|
||
options:
|
||
subject: My %s subject
|
||
recipients:
|
||
your.company@example.com: 'Your Company name'
|
||
ceo@example.com: 'CEO'
|
||
senderAddress: bar@example.org
|
||
translation:
|
||
translationFiles:
|
||
10: path/to/locallang.xlf
|
||
arguments:
|
||
subject:
|
||
- awesome
|
||
|
||
This will produce `My awesome subject`.
|
||
|
||
.. _concepts-frontendrendering-basiccodecomponents:
|
||
|
||
Basic code components
|
||
=====================
|
||
|
||
.. figure:: ../../Images/basic_code_components.png
|
||
:alt: Basic code components
|
||
|
||
Basic code components
|
||
|
||
.. _concepts-frontendrendering-basiccodecomponents-formdefinition:
|
||
|
||
TYPO3\\CMS\\Form\\Domain\\Model\\FormDefinition
|
||
-----------------------------------------------
|
||
|
||
The class :php:`TYPO3\CMS\Form\Domain\Model\FormDefinition` encapsulates
|
||
a complete `form definition`, with all of its
|
||
|
||
* pages,
|
||
* form elements,
|
||
* validation rules, and
|
||
* finishers which are executed when the form is submitted.
|
||
|
||
The FormDefinition domain model is not modified when the form is executed.
|
||
|
||
.. _concepts-frontendrendering-basiccodecomponents-formdefinition-anatomy:
|
||
|
||
The anatomy of a form
|
||
~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
A `FormDefinition` domain model consists of multiple `Page` objects.
|
||
When a form is displayed, only one `Page` is visible at a time.
|
||
However, you can navigate back and forth between the pages. A
|
||
`Page` consists of multiple `FormElements` which represent input
|
||
fields, textareas, checkboxes, etc, on a page. The `FormDefinition`
|
||
domain model, `Page` and `FormElement` objects have `identifiers`
|
||
which must be unique for each `<formElementTypeIdentifier>`,
|
||
i.e. the `FormDefinition` domain model and a `FormElement` object may
|
||
have the same `identifier` but two `FormElement` objects cannot have the same
|
||
identifier.
|
||
|
||
.. _concepts-frontendrendering-basiccodecomponents-formdefinition-anatomy-example:
|
||
|
||
Example
|
||
"""""""
|
||
|
||
You can create a :php:`FormDefinition` domain model by calling the API methods
|
||
on it, or you can use a :php:`FormFactory` to build the form from a different
|
||
representation format such as YAML. The example below calls API methods to
|
||
add a page to a :php:`FormDefinition` and then to add an element to the page:
|
||
|
||
.. code-block:: php
|
||
|
||
$formDefinition = GeneralUtility::makeInstance(FormDefinition::class, 'myForm');
|
||
|
||
$page1 = GeneralUtility::makeInstance(Page::class, 'page1');
|
||
$formDefinition->addPage($page);
|
||
|
||
// second argument is the <formElementTypeIdentifier> of the form element
|
||
$element1 = GeneralUtility::makeInstance(GenericFormElement::class, 'title', 'Text');
|
||
$page1->addElement($element1);
|
||
|
||
.. _concepts-frontendrendering-basiccodecomponents-formdefinition-createformusingabstracttypes:
|
||
|
||
Creating a form using abstract form element types
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
You can use the :php:`TYPO3\CMS\Form\Domain\Model\FormDefinition::addPage()`
|
||
and :php:`TYPO3\CMS\Form\Domain\Model\FormElements\Page::addElement()` methods as above
|
||
and create the `Page` and `FormElement` objects manually, but it is often
|
||
better to use the corresponding *create* methods (:php:`TYPO3\CMS\Form\Domain\Model\FormDefinition::createPage()`
|
||
and :php:`TYPO3\CMS\Form\Domain\Model\FormElements\Page::createElement()`).
|
||
You only need to pass them an abstract `<formElementTypeIdentifier>` such as `Text`
|
||
or `Page` and EXT:form will resolve the classname and set default values.
|
||
|
||
The :ref:`simple example <concepts-frontendrendering-basiccodecomponents-formdefinition-anatomy-example>`
|
||
shown above can then be rewritten as follows:
|
||
|
||
.. code-block:: php
|
||
|
||
// we will come back to this later on
|
||
$prototypeConfiguration = [];
|
||
|
||
$formDefinition = GeneralUtility::makeInstance(FormDefinition::class, 'myForm', $prototypeConfiguration);
|
||
$page1 = $formDefinition->createPage('page1');
|
||
$element1 = $page1->addElement('title', 'Text');
|
||
|
||
You might wonder how the system knows that the element `Text` is
|
||
implemented with a `GenericFormElement`. This is configured in the
|
||
:php:`$prototypeConfiguration`. To make the example from above actually work,
|
||
we need to add some meaningful values to :php:`$prototypeConfiguration`:
|
||
|
||
.. code-block:: php
|
||
|
||
$prototypeConfiguration = [
|
||
'formElementsDefinition' => [
|
||
'Page' => [
|
||
'implementationClassName' => 'TYPO3\CMS\Form\Domain\Model\FormElements\Page'
|
||
],
|
||
'Text' => [
|
||
'implementationClassName' => 'TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement'
|
||
],
|
||
],
|
||
];
|
||
|
||
For each abstract `<formElementTypeIdentifier>`, we have to add some
|
||
configuration. In the snippet above, we only define the `implementation
|
||
class name`. Apart from that, it is always possible to set default values
|
||
for all configuration options of such elements, as the following example
|
||
shows:
|
||
|
||
.. code-block:: php
|
||
|
||
$prototypeConfiguration = [
|
||
'formElementsDefinition' => [
|
||
'Page' => [
|
||
'implementationClassName' => 'TYPO3\CMS\Form\Domain\Model\FormElements\Page',
|
||
'label' => 'This is the label of the page if nothing else is specified'
|
||
],
|
||
'Text' => [
|
||
'implementationClassName' => 'TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement',
|
||
'label' = >'Default Label',
|
||
'defaultValue' => 'Default form element value',
|
||
'properties' => [
|
||
'placeholder' => 'Text that is shown if element is empty'
|
||
],
|
||
],
|
||
],
|
||
];
|
||
|
||
.. _concepts-frontendrendering-basiccodecomponents-formdefinition-preconfiguredconfiguration:
|
||
|
||
Using pre-configured $prototypeConfiguration
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Often, it does not make sense to manually create the $prototypeConfiguration
|
||
array. Bigger parts of this array are pre-configured in the extensions's
|
||
YAML settings. The :php:`TYPO3\CMS\Form\Domain\Configuration\ConfigurationService`
|
||
contains helper methods which return the ready-to-use :php`$prototypeConfiguration`.
|
||
|
||
.. _concepts-frontendrendering-basiccodecomponents-formdefinition-rednering:
|
||
|
||
Rendering a FormDefinition
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
To trigger the rendering of a :php:`FormDefinition` domain model, the current
|
||
:php:`TYPO3\CMS\Extbase\Mvc\Web\Request` needs to be bound to the
|
||
`FormDefinition`. This binding results in a :php:`TYPO3\CMS\Form\Domain\Runtime\FormRuntime`
|
||
object which contains the `Runtime State` of the form. Among other things,
|
||
this object includes the currently inserted values:
|
||
|
||
.. code-block:: php
|
||
|
||
// $currentRequest needs to be available.
|
||
// Inside a controller, you would use $this->request
|
||
$form = $formDefinition->bind($currentRequest);
|
||
// now, you can use the $form object to get information about the currently entered values, etc.
|
||
|
||
.. _concepts-frontendrendering-basiccodecomponents-formruntime:
|
||
|
||
TYPO3\\CMS\\Form\\Domain\\Runtime\\FormRuntime
|
||
----------------------------------------------
|
||
|
||
This class implements the runtime logic of a form, i.e. the class
|
||
|
||
* decides which page is currently shown,
|
||
* determines the current values of the form
|
||
* triggers validation and property mappings.
|
||
|
||
You generally receive an instance of this class by
|
||
calling :php:`TYPO3\CMS\Form\Domain\Model\FormDefinition::bind()`.
|
||
|
||
.. _concepts-frontendrendering-basiccodecomponents-formruntime-render:
|
||
|
||
Rendering a form
|
||
~~~~~~~~~~~~~~~~
|
||
|
||
Rendering a form is easy. Just call :php:`render()` on the :php:`FormRuntime`::
|
||
|
||
.. code-block:: php
|
||
|
||
$form = $formDefinition->bind($request);
|
||
$renderedForm = $form->render();
|
||
|
||
.. _concepts-frontendrendering-basiccodecomponents-formruntime-accessingformvalues:
|
||
|
||
Accessing form values
|
||
~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
In order to get the values the user has entered into the form, you can
|
||
access the :php:`FormRuntime` object like an array. If a form element with the
|
||
identifier `firstName` exists, you can use :php:`$form['firstName']` to
|
||
retrieve its current value. You can set values the same way.
|
||
|
||
.. _concepts-frontendrendering-basiccodecomponents-formruntime-renderinginternals:
|
||
|
||
Rendering internals
|
||
~~~~~~~~~~~~~~~~~~~
|
||
|
||
The :php:`FormRuntime` inquires the :php:`FormDefinition` domain model regarding
|
||
the configured renderer (:php:`TYPO3\CMS\Form\Domain\Model\FormDefinition::getRendererClassName()`)
|
||
and then triggers :php:`render()` on this Renderer.
|
||
|
||
This allows you to declaratively define how a form should be rendered.
|
||
|
||
.. code-block:: yaml
|
||
|
||
prototypes:
|
||
standard:
|
||
formElementsDefinition:
|
||
Form:
|
||
rendererClassName: 'TYPO3\CMS\Form\Domain\Renderer\FluidFormRenderer'
|
||
|
||
.. _concepts-frontendrendering-basiccodecomponents-fluidformrenderer:
|
||
|
||
TYPO3\\CMS\\Form\\Domain\\Renderer\\FluidFormRenderer
|
||
-----------------------------------------------------
|
||
|
||
This class is a :php:`TYPO3\CMS\Form\Domain\Renderer\RendererInterface`
|
||
implementation which used to render a :php:`FormDefinition` domain model. It
|
||
is the default :t3ext:`form` renderer.
|
||
|
||
Learn more about
|
||
the :ref:`FluidFormRenderer Options<apireference-frontendrendering-fluidformrenderer-options>`.
|
||
|
||
.. _concepts-frontendrendering-codecomponents-customformelementimplementations:
|
||
|
||
Custom form element implementations
|
||
-----------------------------------
|
||
|
||
PSR-14 events are available at crucial points in the life cycle of a
|
||
`FormElement`. Most of the time, own class implementations are therefore
|
||
unnecessary. A custom form element can be defined by:
|
||
|
||
* writing some configuration, and
|
||
* utilizing the standard implementation of :php:`TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement`.
|
||
|
||
.. code-block:: yaml
|
||
|
||
prototypes:
|
||
standard:
|
||
formElementsDefinition:
|
||
CustomFormElementIdentifier:
|
||
implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement'
|
||
|
||
With the provided PSR-14 events, this `FormElement` can now be manipulated at runtime.
|
||
|
||
.. seealso::
|
||
* :ref:`PSR-14 events overview for EXT:form <apireference-events>` –
|
||
tables of all events and registration instructions
|
||
* :ref:`Runtime manipulation events <apireference-frontendrendering-runtimemanipulation-events>`
|
||
|
||
If you insist on your own implementation, the abstract class :php:`TYPO3\CMS\Form\Domain\Model\FormElements\AbstractFormElement`
|
||
offers a perfect entry point. In addition, we recommend checking-out :php:`TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable`.
|
||
All of your own form element implementations must be programmed to the
|
||
interface :php:`TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface`.
|
||
It is a good idea to derive your implementation from :php:`TYPO3\CMS\Form\Domain\Model\FormElements\AbstractFormElement`.
|
||
|
||
.. _concepts-frontendrendering-renderviewHelper:
|
||
|
||
"render" viewHelper
|
||
===================
|
||
|
||
The `RenderViewHelper` is the actual starting point for form rendering and
|
||
not the typical Extbase Controller as you may know it.
|
||
|
||
For more technical insights read more about the viewHelper's :ref:`arguments<apireference-frontendrendering-renderviewHelper-arguments>`.
|
||
|
||
.. _concepts-frontendrendering-fluidtemplate:
|
||
|
||
Render through FLUIDTEMPLATE (without controller)
|
||
-------------------------------------------------
|
||
|
||
.. code-block:: typoscript
|
||
|
||
tt_content.custom_content_element = COA_INT
|
||
tt_content.custom_content_element {
|
||
20 = FLUIDTEMPLATE
|
||
20 {
|
||
file = EXT:my_site_package/Resources/Private/Templates/CustomContentElement.html
|
||
settings {
|
||
persistenceIdentifier = EXT:my_site_package/Resources/Private/Forms/MyForm.yaml
|
||
}
|
||
extbase.pluginName = Formframework
|
||
extbase.controllerExtensionName = Form
|
||
extbase.controllerName = FormFrontend
|
||
extbase.controllerActionName = perform
|
||
}
|
||
}
|
||
|
||
``my_site_package/Resources/Private/Templates/CustomContentElement.html``:
|
||
|
||
.. code-block:: html
|
||
|
||
<formvh:render persistenceIdentifier="{settings.persistenceIdentifier}" />
|
||
|
||
.. _concepts-frontendrendering-extbase:
|
||
|
||
Render within your own Extbase extension
|
||
----------------------------------------
|
||
|
||
It is straight forward. Use the `RenderViewHelper` like this and you are
|
||
done:
|
||
|
||
.. code-block:: html
|
||
|
||
<formvh:render persistenceIdentifier="EXT:my_site_package/Resources/Private/Forms/MyForm.yaml"/>
|
||
|
||
Point the property `controllerAction` to the desired action name and
|
||
provide values for the other parameters displayed below (you might need
|
||
those).
|
||
|
||
.. code-block:: yaml
|
||
|
||
type: Form
|
||
identifier: 'example-form'
|
||
label: 'TYPO3 is cool'
|
||
prototypeName: standard
|
||
renderingOptions:
|
||
controllerAction: perform
|
||
addQueryString: false
|
||
argumentsToBeExcludedFromQueryString: []
|
||
additionalParams: []
|
||
|
||
renderables:
|
||
...
|
||
|
||
.. note::
|
||
|
||
In general, you can override each and every `form definition` with the help
|
||
of TypoScript (see ':ref:`TypoScript overrides<concepts-frontendrendering-runtimemanipulation-typoscriptoverrides>`').
|
||
|
||
When using the `RenderViewHelper`, there is a second way:
|
||
The ':ref:`overrideConfiguration<apireference-frontendrendering-renderviewHelper-overrideconfiguration>`' parameter.
|
||
This way, you can override the form definition within your template.
|
||
Provide an according array as shown in the example below.
|
||
|
||
.. code-block:: html
|
||
|
||
<formvh:render persistenceIdentifier="EXT:my_site_package/Resources/Private/Forms/MyForm.yaml" overrideConfiguration="{renderables: {0: {renderables: {0: {label: 'My shiny new label'}}}}}"/>
|
||
|
||
.. _concepts-frontendrendering-programmatically:
|
||
|
||
Build forms programmatically
|
||
============================
|
||
|
||
To learn more about this topic, head to the chapter ':ref:`Build forms programmatically<apireference-frontendrendering-programmatically>`'
|
||
which is part of the API reference section.
|
||
|
||
.. _concepts-frontendrendering-runtimemanipulation:
|
||
|
||
Runtime manipulation
|
||
====================
|
||
|
||
.. _concepts-frontendrendering-runtimemanipulation-hooks:
|
||
|
||
:t3ext:`form` implements a decent amount of events that allow the manipulation of
|
||
your forms during runtime. In this way, it is possible to, for example,
|
||
|
||
* ... prefill form elements with values from your database,
|
||
* ... skip a whole page based on the value of a certain form element,
|
||
* ... mark a form element as mandatory depending of the chosen value of another
|
||
form element.
|
||
|
||
Please check out the :ref:`PSR-14 events overview <apireference-events>`
|
||
for more details.
|
||
|
||
.. _concepts-frontendrendering-runtimemanipulation-typoscriptoverrides:
|
||
|
||
TypoScript overrides
|
||
--------------------
|
||
|
||
Each and every `form definition` can be overridden via TypoScript if the
|
||
:php:`FormFrontendController` of :t3ext:`form` is used to render the form. Normally,
|
||
this is the case if the form has been added to the page using the form
|
||
plugin or when rendering the form via :ref:`FLUIDTEMPLATE <concepts-frontendrendering-fluidtemplate>`.
|
||
|
||
The overriding of settings with TypoScript's help takes place after the :ref:`custom finisher settings<concepts-formplugin>`
|
||
of the form plugin have been loaded. In this way, you are able to manipulate
|
||
the `form definition` for a single page. In doing so, the altered
|
||
`form definition` is passed to the :php:`RenderViewHelper` which then
|
||
generates the form programmatically. At this point, you can still change the
|
||
form elements using the above-mentioned concept of :ref:`hooks<concepts-frontendrendering-runtimemanipulation-hooks>`.
|
||
|
||
.. code-block:: typoscript
|
||
|
||
plugin.tx_form {
|
||
settings {
|
||
formDefinitionOverrides {
|
||
<formDefinitionIdentifier> {
|
||
renderables {
|
||
0 {
|
||
renderables {
|
||
0 {
|
||
label = TEXT
|
||
label.value = Overridden label
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|