TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,937 @@
|
||||
.. 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user