diff --git a/src/Forms/Controls/RadioList.php b/src/Forms/Controls/RadioList.php
index 3be94c26..ef6b8586 100644
--- a/src/Forms/Controls/RadioList.php
+++ b/src/Forms/Controls/RadioList.php
@@ -27,6 +27,7 @@ class RadioList extends ChoiceControl
protected Html $separator;
protected Html $container;
protected Html $itemLabel;
+ private string|Stringable|false $emptyItem = false;
public function __construct(string|Stringable|null $label = null, ?array $items = null)
@@ -40,6 +41,25 @@ public function __construct(string|Stringable|null $label = null, ?array $items
}
+ /**
+ * Sets the first N/A item.
+ */
+ public function setEmptyItem(string|Stringable|false $label): static
+ {
+ $this->emptyItem = $label;
+ return $this;
+ }
+
+
+ /**
+ * Returns the first N/A item.
+ */
+ public function getEmptyItem(): string|Stringable|false
+ {
+ return $this->emptyItem;
+ }
+
+
public function getControl(): Html
{
$input = parent::getControl();
@@ -51,12 +71,25 @@ public function getControl(): Html
}
}
+ $selected = $this->value;
+ if ($this->emptyItem !== false) {
+ if ($this->isRequired()) {
+ throw new Nette\InvalidStateException('Required radiolist cannot have empty item.');
+ }
+ $emptyKey = '';
+ while (isset($items[$emptyKey])) {
+ $emptyKey .= "\x1";
+ }
+ $items = [$emptyKey => $this->translate($this->emptyItem)] + $items;
+ $selected ??= $emptyKey;
+ }
+
return $this->container->setHtml(
Nette\Forms\Helpers::createInputList(
$this->translate($items),
array_merge($input->attrs, [
'id:' => $ids,
- 'checked?' => $this->value,
+ 'checked?' => $selected,
'disabled:' => $this->disabled,
'data-nette-rules:' => [key($items) => $input->attrs['data-nette-rules']],
]),
diff --git a/tests/Forms/Controls.RadioList.loadData.phpt b/tests/Forms/Controls.RadioList.loadData.phpt
index 14b24cf0..ba507229 100644
--- a/tests/Forms/Controls.RadioList.loadData.phpt
+++ b/tests/Forms/Controls.RadioList.loadData.phpt
@@ -141,6 +141,17 @@ testException('setValue() and invalid argument', function () use ($series) {
}, Nette\InvalidArgumentException::class, "Value 'unknown' is out of allowed set ['red-dwarf', 'the-simpsons', 0, ''] in field 'radio'.");
+test('empty item selected', function () {
+ $_POST = ['radio' => ''];
+
+ $form = new Form;
+ $input = $form->addRadioList('radio', null, ['a' => 'First'])->setEmptyItem('n/a');
+
+ Assert::true($form->isValid());
+ Assert::null($input->getValue());
+});
+
+
test('object as value', function () {
$form = new Form;
$input = $form->addRadioList('radio', null, ['2013-07-05 00:00:00' => 1])
diff --git a/tests/Forms/Controls.RadioList.render.phpt b/tests/Forms/Controls.RadioList.render.phpt
index 024bdece..0bfc5980 100644
--- a/tests/Forms/Controls.RadioList.render.phpt
+++ b/tests/Forms/Controls.RadioList.render.phpt
@@ -88,6 +88,23 @@ test('Html', function () {
});
+test('empty item', function () {
+ $form = new Form;
+ $input = $form->addRadioList('list', 'Label', [
+ '' => 'First',
+ ])->setEmptyItem('n/a');
+
+ Assert::same('
', (string) $input->getControl());
+
+ $input->setRequired();
+ Assert::exception(
+ fn() => $input->getControl(),
+ Nette\InvalidStateException::class,
+ 'Required radiolist cannot have empty item.',
+ );
+});
+
+
test('validation rules', function () {
$form = new Form;
$input = $form->addRadioList('list', 'Label', [