Skip to content

Commit 0f5181d

Browse files
[TASK] Update f:form.checkbox ViewHelper page (#120)
* [TASK] Update f:form.checkbox ViewHelper page * Supply examples including controller actions and models * optional / required checkboxes * single / multiple checkboxes * binding to action argument or model property Releases: main, 13.4 * Apply suggestions from code review Co-authored-by: Garvin Hicking <[email protected]> * Apply suggestions from code review * Update Documentation/Global/Form/Checkbox.rst Co-authored-by: Garvin Hicking <[email protected]> --------- Co-authored-by: Garvin Hicking <[email protected]>
1 parent 27f3c54 commit 0f5181d

10 files changed

+365
-23
lines changed

Documentation/Global/Form/Checkbox.rst

Lines changed: 201 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,46 +9,224 @@ Form.checkbox ViewHelper `<f:form.checkbox>`
99

1010
.. typo3:viewhelper:: form.checkbox
1111
:source: ../../Global.json
12-
:display: tags,description,gitHubLink,arguments
12+
:display: tags,description,gitHubLink
13+
:noindex:
14+
15+
.. contents:: Table of contents
16+
17+
.. _typo3-fluid-form-checkbox-single:
18+
19+
Single checkboxes
20+
=================
21+
22+
A single checkbox is usually mapped to a boolean property or controller
23+
argument.
24+
25+
However due to how checkboxes work in HTML a non-checked checkbox does not
26+
appear in the request sent by the form submission. Therefore properties and arguments
27+
need to default to `false`. They will then be set to `true` if the checkbox
28+
was checked.
29+
30+
While the :ref:`value <t3viewhelper:viewhelper-argument-typo3-cms-fluid-viewhelpers-form-checkboxviewhelper-value>`
31+
does not play a role when you are working with a checkbox, the argument must still
32+
be supplied.
1333

1434
.. _typo3-fluid-form-checkbox-example:
1535

16-
Examples
17-
========
36+
Single optional checkbox tied to an action argument
37+
--------------------------------------------------
38+
39+
Checkboxes in HTML work a little different from other input fields in that
40+
multiple or none at all can be checked.
41+
42+
Fluid outputs a normal HTML `<input type="checkbox" ...><https://www.w3schools.com/tags/att_input_type_checkbox.asp>`_
43+
and some of the pitfalls also apply here. Especially a check box that is not
44+
sends no argument to the request.
45+
46+
.. tabs::
47+
48+
.. group-tab:: Fluid
49+
50+
.. literalinclude:: _codesnippets/_CheckboxOptional.html
51+
:caption: packages/my_extension/Resources/Private/Templates/Comment/Newsletter.html
52+
53+
.. group-tab:: Controller
54+
55+
Then the controller action can then look like this:
56+
57+
.. literalinclude:: _codesnippets/_CheckboxController.php
58+
:caption: packages/my_extension/Classes/Controller/NewsletterController.php
59+
60+
.. note::
61+
If a checkbox is not checked it sends NO argument as opposed to a text input
62+
with no text entered. Therefore in the Extbase action you **must** supply
63+
a default for the argument if non-checked fields should be allowed.
64+
65+
.. _typo3-fluid-form-checkbox-example-preselected:
66+
67+
The checkbox should already by checked / preselected
68+
----------------------------------------------------
69+
70+
.. code-block:: html
71+
72+
<f:form.checkbox name="accept" value="yes" checked="true" />
73+
74+
You can also use a variable to determine whether the field should be
75+
checked:
76+
77+
.. code-block:: html
78+
79+
<f:form.checkbox name="accept" value="yes" checked="{myVariable} == 5" />
80+
81+
.. note::
82+
When you work with `Property mapping <https://docs.typo3.org/permalink/t3viewhelper:typo3-fluid-form-checkbox-property>`_
83+
the checkbox is automatically preselected if the model's property evalutes to `true`.
84+
85+
.. _typo3-fluid-form-checkbox-property:
86+
87+
Property mapping - domain model property bound to single checkbox
88+
-----------------------------------------------------------------
89+
90+
If you are using `the form with a model <https://docs.typo3.org/permalink/t3viewhelper:typo3-fluid-form-example-property>`_
91+
you can use the argument :ref:`property <t3viewhelper:viewhelper-argument-typo3-cms-fluid-viewhelpers-form-checkboxviewhelper-property>`
92+
to map the checkbox to a property of your domain model or data object:
93+
94+
.. tabs::
95+
96+
.. group-tab:: Fluid
97+
98+
.. literalinclude:: _codesnippets/_CheckboxProperty.html
99+
:caption: packages/my_extension/Resources/Private/Templates/Newsletter/SomeForm.html
100+
101+
.. group-tab:: Controller
102+
103+
Then the controller action can look like this:
18104

19-
Simple one
20-
----------
105+
.. literalinclude:: _codesnippets/_CheckboxModelController.php
106+
:caption: packages/my_extension/Classes/Controller/NewsletterController.php
21107

22-
::
108+
.. group-tab:: Model
23109

24-
<f:form.checkbox name="myCheckBox" value="someValue" />
110+
An unchecked checkbox results in the property not being set. It should
111+
therefore default to `false`.
25112

26-
Output::
113+
.. literalinclude:: _codesnippets/_CheckboxModelUser.php
114+
:caption: packages/my_extension/Classes/Domain/Model/User.php
27115

28-
<input type="checkbox" name="myCheckBox" value="someValue" />
116+
If the property in the domain model is `true` when the form is displayed, the
117+
checkbox is preselected.
29118

30-
Preselect
31-
---------
119+
.. _typo3-fluid-form-checkbox-multiple:
32120

33-
::
121+
Multiple checkboxes for the same property
122+
=========================================
34123

35-
<f:form.checkbox name="myCheckBox" value="someValue" checked="{object.value} == 5" />
124+
Unlike other input elements, multiple checkboxes can be used for the same
125+
property or action argument.
36126

37-
Output::
127+
In this case they share the same :ref:`name <t3viewhelper:viewhelper-argument-typo3-cms-fluid-viewhelpers-form-checkboxviewhelper-name>`
128+
or property binding :ref:`property <t3viewhelper:viewhelper-argument-typo3-cms-fluid-viewhelpers-form-checkboxviewhelper-property>`
129+
but have distinct :ref:`values <t3viewhelper:viewhelper-argument-typo3-cms-fluid-viewhelpers-form-checkboxviewhelper-value>`.
38130

39-
<input type="checkbox" name="myCheckBox" value="someValue" checked="checked" />
131+
If you are working with action arguments, :ref:`multiple <t3viewhelper:viewhelper-argument-typo3-cms-fluid-viewhelpers-form-checkboxviewhelper-multiple>`
132+
must be set.
40133

41-
Depending on bound ``object`` to surrounding :ref:`f:form <typo3-fluid-form>`.
134+
.. _typo3-fluid-form-checkbox-multiple-action:
42135

43-
Bind to object property
44-
-----------------------
136+
Multiple checkboxes mapped to an array in a controller action
137+
-------------------------------------------------------------
45138

46-
::
139+
Multiple checkboxes are usually mapped to an `array`. It would however be possible, for example,
140+
to map them to an integer using binaries or such.
47141

48-
<f:form.checkbox property="interests" value="TYPO3" multiple="1" />
142+
Therefore when working with multiple checkboxes and arrays, you have to tell
143+
Extbase how to map the data from the request to your controller action in an
144+
`initialize` action.
49145

50-
Output::
146+
.. tabs::
51147

52-
<input type="checkbox" name="user[interests][]" value="TYPO3" checked="checked" />
148+
.. group-tab:: Fluid
53149

54-
Depending on property ``interests``.
150+
.. literalinclude:: _codesnippets/_CheckboxMultiple.html
151+
:caption: packages/my_extension/Resources/Private/Templates/Newsletter/SomeForm.html
152+
153+
.. group-tab:: Controller
154+
155+
Then the controller action can look like this:
156+
157+
.. literalinclude:: _codesnippets/_CheckboxMultipleController.php
158+
:caption: packages/my_extension/Classes/Controller/NewsletterController.php
159+
160+
.. note::
161+
See class :php:`\TYPO3\CMS\Extbase\Property\TypeConverter\ArrayConverter`
162+
for details on mapping arrays.
163+
164+
.. _typo3-fluid-form-checkbox-multiple-property:
165+
166+
Property mapping of multiple checkboxes
167+
---------------------------------------
168+
169+
When working with multiple checkboxes mapped to the property of an Extbase model
170+
or data object, the same
171+
:ref:`property <t3viewhelper:viewhelper-argument-typo3-cms-fluid-viewhelpers-form-checkboxviewhelper-property>`
172+
is used for all checkboxes to be mapped to that property.
173+
174+
.. note::
175+
An example is still missing. **You** can help by providing an example.
176+
177+
Click the "report issue" button above and hand in your examples.
178+
179+
.. _typo3-fluid-form-checkbox-multiple-independent:
180+
181+
Multiple checkboxes for multiple independent properties
182+
=======================================================
183+
184+
When multiple checkboxes should be used independently they must have unique
185+
:ref:`name <t3viewhelper:viewhelper-argument-typo3-cms-fluid-viewhelpers-form-checkboxviewhelper-name>`
186+
properties to map to multiple action arguments or unique
187+
:ref:`property <t3viewhelper:viewhelper-argument-typo3-cms-fluid-viewhelpers-form-checkboxviewhelper-property>`
188+
values to bind to multiple properties.
189+
190+
.. _typo3-fluid-form-checkbox-mandatory:
191+
192+
Mandatory checkboxes - require a checkbox to be set
193+
===================================================
194+
195+
On the browser side you can use the `HTML 5 "required" Attribute <https://www.w3schools.com/tags/att_input_required.asp>`_.
196+
As the `<f:form.checkbox>` ViewHelper allows arbitrary arguments, using
197+
the `required` property is possible even though it is not listed.
198+
199+
.. tabs::
200+
201+
.. group-tab:: Fluid
202+
203+
.. literalinclude:: _codesnippets/_CheckboxRequired.html
204+
:caption: packages/my_extension/Resources/Private/Templates/Newsletter/SomeForm.html
205+
206+
.. group-tab:: Controller
207+
208+
Then the controller action can then look like this:
209+
210+
.. literalinclude:: _codesnippets/_CheckboxModelController.php
211+
:caption: packages/my_extension/Classes/Controller/NewsletterController.php
212+
213+
.. group-tab:: Model
214+
215+
You should also validate the model for the property to be true:
216+
217+
.. literalinclude:: _codesnippets/_CheckboxModelUserRequired.php
218+
:caption: packages/my_extension/Classes/Domain/Model/User.php
219+
220+
If the server side validation on the model fails, the request is forwarded to
221+
the originating request with an error message.
222+
223+
.. _typo3-fluid-form-checkbox-arguments:
224+
225+
Arguments
226+
=========
227+
228+
.. include:: /_Includes/_ArbitraryArguments.rst.txt
229+
230+
.. typo3:viewhelper:: form.checkbox
231+
:source: ../../Global.json
232+
:display: arguments-only
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MyVendor\MyExtension\Controller;
6+
7+
use Psr\Http\Message\ResponseInterface;
8+
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
9+
10+
class NewsletterController extends ActionController
11+
{
12+
public function orderNewsletterAction(
13+
// If the checkbox is not checked, no argument is provided.
14+
// Default to false
15+
bool $orderNewsletter = false,
16+
): ResponseInterface {
17+
if ($orderNewsletter) {
18+
// TODO: Newsletter ordering
19+
}
20+
return $this->htmlResponse();
21+
}
22+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MyVendor\MyExtension\Controller;
6+
7+
use MyVendor\MyExtension\Domain\Model\User;
8+
use Psr\Http\Message\ResponseInterface;
9+
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
10+
11+
class NewsletterController extends ActionController
12+
{
13+
public function orderNewsletterAction(
14+
User $user,
15+
): ResponseInterface {
16+
if ($user->isOrderNewsletter()) {
17+
// TODO: Newsletter ordering
18+
}
19+
return $this->htmlResponse();
20+
}
21+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MyVendor\MyExtension\Domain\Model;
6+
7+
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
8+
9+
class User extends AbstractEntity
10+
{
11+
protected bool $orderNewsletter = false;
12+
13+
public function isOrderNewsletter(): bool
14+
{
15+
return $this->orderNewsletter;
16+
}
17+
18+
public function setOrderNewsletter(bool $orderNewsletter): void
19+
{
20+
$this->orderNewsletter = $orderNewsletter;
21+
}
22+
23+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MyVendor\MyExtension\Domain\Model;
6+
7+
use TYPO3\CMS\Extbase\Annotation\Validate;
8+
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
9+
10+
class User extends AbstractEntity
11+
{
12+
#[Validate([
13+
'validator' => 'Boolean',
14+
'options' => ['is' => true],
15+
])]
16+
protected bool $consentGiven = false;
17+
18+
public function isConsentGiven(): bool
19+
{
20+
return $this->consentGiven;
21+
}
22+
23+
public function setConsentGiven(bool $consentGiven): void
24+
{
25+
$this->consentGiven = $consentGiven;
26+
}
27+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<f:form action="orderNewsletter" method="post">
2+
<f:for each="{newsletterList}" as="newsletter" iteration="i">
3+
<div>
4+
<f:form.checkbox name="orderNewsletters" id="newsletter{i.cycle}" value="{newsletter.uid}" />
5+
<label for="newsletter{i.cycle}">{newsletter.title}</label>
6+
</div>
7+
</f:for>
8+
<div><f:form.submit value="Submit" /></div>
9+
</f:form>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MyVendor\MyExtension\Controller;
6+
7+
use Psr\Http\Message\ResponseInterface;
8+
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
9+
use TYPO3\CMS\Extbase\Property\TypeConverter\ArrayConverter;
10+
11+
class NewsletterController extends ActionController
12+
{
13+
public function orderNewsletterAction(
14+
// This argument is mapped in initializeOrderNewsletterAction()
15+
array $orderNewsletters = [],
16+
): ResponseInterface {
17+
if ($orderNewsletters !== []) {
18+
// TODO: Newsletter ordering
19+
}
20+
return $this->htmlResponse();
21+
}
22+
public function initializeOrderNewsletterAction()
23+
{
24+
if ($this->request->hasArgument('orderNewsletters')) {
25+
$this->arguments['orderNewsletters']
26+
->getPropertyMappingConfiguration()
27+
->setTypeConverterOption(
28+
ArrayConverter::class,
29+
ArrayConverter::CONFIGURATION_DELIMITER,
30+
',',
31+
);
32+
}
33+
}
34+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<f:form action="orderNewsletter" method="post">
2+
<div>
3+
<f:form.checkbox name="orderNewsletter" id="orderNewsletter" value="yes" />
4+
<label for="orderNewsletter">Order newsletter</label>
5+
</div>
6+
<div>
7+
<f:form.submit value="Submit" />
8+
</div>
9+
</f:form>

0 commit comments

Comments
 (0)