Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 201 additions & 23 deletions Documentation/Global/Form/Checkbox.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,46 +9,224 @@ Form.checkbox ViewHelper `<f:form.checkbox>`

.. typo3:viewhelper:: form.checkbox
:source: ../../Global.json
:display: tags,description,gitHubLink,arguments
:display: tags,description,gitHubLink
:noindex:

.. contents:: Table of contents

.. _typo3-fluid-form-checkbox-single:

Single checkboxes
=================

A single checkbox is usually mapped to a boolean property or controller
argument.

However due to how checkboxes work in HTML a non-checked checkbox does not
appear in the request sent by the form submission. Therefore properties and arguments
need to default to `false`. They will then be set to `true` if the checkbox
was checked.

While the :ref:`value <t3viewhelper:viewhelper-argument-typo3-cms-fluid-viewhelpers-form-checkboxviewhelper-value>`
does not play a role when you are working with a checkbox, the argument must still
be supplied.

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

Examples
========
Single optional checkbox tied to an action argument
--------------------------------------------------

Checkboxes in HTML work a little different from other input fields in that
multiple or none at all can be checked.

Fluid outputs a normal HTML `<input type="checkbox" ...><https://www.w3schools.com/tags/att_input_type_checkbox.asp>`_
and some of the pitfalls also apply here. Especially a check box that is not
sends no argument to the request.

.. tabs::

.. group-tab:: Fluid

.. literalinclude:: _codesnippets/_CheckboxOptional.html
:caption: packages/my_extension/Resources/Private/Templates/Comment/Newsletter.html

.. group-tab:: Controller

Then the controller action can then look like this:

.. literalinclude:: _codesnippets/_CheckboxController.php
:caption: packages/my_extension/Classes/Controller/NewsletterController.php

.. note::
If a checkbox is not checked it sends NO argument as opposed to a text input
with no text entered. Therefore in the Extbase action you **must** supply
a default for the argument if non-checked fields should be allowed.

.. _typo3-fluid-form-checkbox-example-preselected:

The checkbox should already by checked / preselected
----------------------------------------------------

.. code-block:: html

<f:form.checkbox name="accept" value="yes" checked="true" />

You can also use a variable to determine whether the field should be
checked:

.. code-block:: html

<f:form.checkbox name="accept" value="yes" checked="{myVariable} == 5" />

.. note::
When you work with `Property mapping <https://docs.typo3.org/permalink/t3viewhelper:typo3-fluid-form-checkbox-property>`_
the checkbox is automatically preselected if the model's property evalutes to `true`.

.. _typo3-fluid-form-checkbox-property:

Property mapping - domain model property bound to single checkbox
-----------------------------------------------------------------

If you are using `the form with a model <https://docs.typo3.org/permalink/t3viewhelper:typo3-fluid-form-example-property>`_
you can use the argument :ref:`property <t3viewhelper:viewhelper-argument-typo3-cms-fluid-viewhelpers-form-checkboxviewhelper-property>`
to map the checkbox to a property of your domain model or data object:

.. tabs::

.. group-tab:: Fluid

.. literalinclude:: _codesnippets/_CheckboxProperty.html
:caption: packages/my_extension/Resources/Private/Templates/Newsletter/SomeForm.html

.. group-tab:: Controller

Then the controller action can look like this:

Simple one
----------
.. literalinclude:: _codesnippets/_CheckboxModelController.php
:caption: packages/my_extension/Classes/Controller/NewsletterController.php

::
.. group-tab:: Model

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

Output::
.. literalinclude:: _codesnippets/_CheckboxModelUser.php
:caption: packages/my_extension/Classes/Domain/Model/User.php

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

Preselect
---------
.. _typo3-fluid-form-checkbox-multiple:

::
Multiple checkboxes for the same property
=========================================

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

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

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

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

Bind to object property
-----------------------
Multiple checkboxes mapped to an array in a controller action
-------------------------------------------------------------

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

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

Output::
.. tabs::

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

Depending on property ``interests``.
.. literalinclude:: _codesnippets/_CheckboxMultiple.html
:caption: packages/my_extension/Resources/Private/Templates/Newsletter/SomeForm.html

.. group-tab:: Controller

Then the controller action can look like this:

.. literalinclude:: _codesnippets/_CheckboxMultipleController.php
:caption: packages/my_extension/Classes/Controller/NewsletterController.php

.. note::
See class :php:`\TYPO3\CMS\Extbase\Property\TypeConverter\ArrayConverter`
for details on mapping arrays.

.. _typo3-fluid-form-checkbox-multiple-property:

Property mapping of multiple checkboxes
---------------------------------------

When working with multiple checkboxes mapped to the property of an Extbase model
or data object, the same
:ref:`property <t3viewhelper:viewhelper-argument-typo3-cms-fluid-viewhelpers-form-checkboxviewhelper-property>`
is used for all checkboxes to be mapped to that property.

.. note::
An example is still missing. **You** can help by providing an example.

Click the "report issue" button above and hand in your examples.

.. _typo3-fluid-form-checkbox-multiple-independent:

Multiple checkboxes for multiple independent properties
=======================================================

When multiple checkboxes should be used independently they must have unique
:ref:`name <t3viewhelper:viewhelper-argument-typo3-cms-fluid-viewhelpers-form-checkboxviewhelper-name>`
properties to map to multiple action arguments or unique
:ref:`property <t3viewhelper:viewhelper-argument-typo3-cms-fluid-viewhelpers-form-checkboxviewhelper-property>`
values to bind to multiple properties.

.. _typo3-fluid-form-checkbox-mandatory:

Mandatory checkboxes - require a checkbox to be set
===================================================

On the browser side you can use the `HTML 5 "required" Attribute <https://www.w3schools.com/tags/att_input_required.asp>`_.
As the `<f:form.checkbox>` ViewHelper allows arbitrary arguments, using
the `required` property is possible even though it is not listed.

.. tabs::

.. group-tab:: Fluid

.. literalinclude:: _codesnippets/_CheckboxRequired.html
:caption: packages/my_extension/Resources/Private/Templates/Newsletter/SomeForm.html

.. group-tab:: Controller

Then the controller action can then look like this:

.. literalinclude:: _codesnippets/_CheckboxModelController.php
:caption: packages/my_extension/Classes/Controller/NewsletterController.php

.. group-tab:: Model

You should also validate the model for the property to be true:

.. literalinclude:: _codesnippets/_CheckboxModelUserRequired.php
:caption: packages/my_extension/Classes/Domain/Model/User.php

If the server side validation on the model fails, the request is forwarded to
the originating request with an error message.

.. _typo3-fluid-form-checkbox-arguments:

Arguments
=========

.. include:: /_Includes/_ArbitraryArguments.rst.txt

.. typo3:viewhelper:: form.checkbox
:source: ../../Global.json
:display: arguments-only
22 changes: 22 additions & 0 deletions Documentation/Global/Form/_codesnippets/_CheckboxController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;

class NewsletterController extends ActionController
{
public function orderNewsletterAction(
// If the checkbox is not checked, no argument is provided.
// Default to false
bool $orderNewsletter = false,
): ResponseInterface {
if ($orderNewsletter) {
// TODO: Newsletter ordering
}
return $this->htmlResponse();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use MyVendor\MyExtension\Domain\Model\User;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;

class NewsletterController extends ActionController
{
public function orderNewsletterAction(
User $user,
): ResponseInterface {
if ($user->isOrderNewsletter()) {
// TODO: Newsletter ordering
}
return $this->htmlResponse();
}
}
23 changes: 23 additions & 0 deletions Documentation/Global/Form/_codesnippets/_CheckboxModelUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Model;

use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;

class User extends AbstractEntity
{
protected bool $orderNewsletter = false;

public function isOrderNewsletter(): bool
{
return $this->orderNewsletter;
}

public function setOrderNewsletter(bool $orderNewsletter): void
{
$this->orderNewsletter = $orderNewsletter;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Model;

use TYPO3\CMS\Extbase\Annotation\Validate;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;

class User extends AbstractEntity
{
#[Validate([
'validator' => 'Boolean',
'options' => ['is' => true],
])]
protected bool $consentGiven = false;

public function isConsentGiven(): bool
{
return $this->consentGiven;
}

public function setConsentGiven(bool $consentGiven): void
{
$this->consentGiven = $consentGiven;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<f:form action="orderNewsletter" method="post">
<f:for each="{newsletterList}" as="newsletter" iteration="i">
<div>
<f:form.checkbox name="orderNewsletters" id="newsletter{i.cycle}" value="{newsletter.uid}" />
<label for="newsletter{i.cycle}">{newsletter.title}</label>
</div>
</f:for>
<div><f:form.submit value="Submit" /></div>
</f:form>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Property\TypeConverter\ArrayConverter;

class NewsletterController extends ActionController
{
public function orderNewsletterAction(
// This argument is mapped in initializeOrderNewsletterAction()
array $orderNewsletters = [],
): ResponseInterface {
if ($orderNewsletters !== []) {
// TODO: Newsletter ordering
}
return $this->htmlResponse();
}
public function initializeOrderNewsletterAction()
{
if ($this->request->hasArgument('orderNewsletters')) {
$this->arguments['orderNewsletters']
->getPropertyMappingConfiguration()
->setTypeConverterOption(
ArrayConverter::class,
ArrayConverter::CONFIGURATION_DELIMITER,
',',
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<f:form action="orderNewsletter" method="post">
<div>
<f:form.checkbox name="orderNewsletter" id="orderNewsletter" value="yes" />
<label for="orderNewsletter">Order newsletter</label>
</div>
<div>
<f:form.submit value="Submit" />
</div>
</f:form>
Loading