Skip to content

Commit

Permalink
Add method uncheckValue(), value(), update docs Checkbox widget. (#132
Browse files Browse the repository at this point in the history
)
  • Loading branch information
terabytesoftw committed Oct 28, 2021
1 parent eaac104 commit 0f1397b
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 63 deletions.
42 changes: 17 additions & 25 deletions docs/checkbox.md
Expand Up @@ -4,13 +4,9 @@

## Usage

The `Checkbox` widget is designed to return the status of the checkbox. Generally it returns two values, by default it is `0` when no value is selected, and `1` otherwise.
The `Checkbox` widget is designed to return the status of the checkbox. Generally it returns two values, by default it is `0` for unchecked, and `1` for checkeded.

You can also configure the widget to return the value only when the checkbox is selected.

### Example Checkbox with default values `0` and `1`:

TestForm class:
You can also configure the widget to return the value only when the checkbox is checked.

```php
<?php
Expand Down Expand Up @@ -42,18 +38,18 @@ use Yiisoft\Form\Widget\Label;

/**
* @var FormModelInterface $data
* @var string $csrf
* @var object $csrf
*/
?>

<?= Form::widget()->action('widgets')->csrf($csrf)->begin(); ?>
<?= Checkbox::widget()->config($data, 'active', [])->render(); ?>
<?= Checkbox::widget()->config($data, 'active')->render(); ?>
<hr class="mt-3">
<?= Field::widget()->submitButton(['class' => 'button is-block is-info is-fullwidth', 'value' => 'Save']); ?>
<?= Form::end(); ?>
```

### Code generated by `Checkbox` Widget
That would generate the following code:

```html
<form action="widgets" method="POST" _csrf="H4r91n-qkv8GzgEU8E3hhn2vSSu5jnkguqemRBysYa1OwJWHR8Txmk6kUSWGHdfnT54zYPXoDkqN1v4CefZR2w==">
Expand All @@ -67,11 +63,9 @@ use Yiisoft\Form\Widget\Label;
</form>
```

As we can see in the code we have the value `0` for the hidden input and `1` for the input checkbox tag, correctly.

### Example Checkbox with default values `1` when the checkbox is checked:
### Custom values

TestForm class:
Custom values could be set as well. In the following example we use `1` when checkbox only checked.

```php
<?php
Expand Down Expand Up @@ -103,18 +97,18 @@ use Yiisoft\Form\Widget\Label;

/**
* @var FormModelInterface $data
* @var string $csrf
* @var object $csrf
*/
?>

<?= Form::widget()->action('widgets')->csrf($csrf)->begin(); ?>
<?= Checkbox::widget()->config($data, 'active', ['forceUncheckedValue' => null])->render(); ?>
<?= Checkbox::widget()->config($data, 'active', ['uncheckValue' => null])->render(); ?>
<hr class="mt-3">
<?= Field::widget()->submitButton(['class' => 'button is-block is-info is-fullwidth', 'value' => 'Save']); ?>
<?= Form::end(); ?>
```

### Code generated by `Checkbox` Widget
That would generate the following code:

```html
<form action="widgets" method="POST" _csrf="Oq_3knkum-2MVDBcu5v_Zbdv4j4NQWKyueB0wrSmExhr5Z_DQUD4iMQ-YG3Ny8kEhV6YdUEnFdiOkSyE0fwjbg==">
Expand All @@ -127,11 +121,9 @@ use Yiisoft\Form\Widget\Label;
</form>
```

As we can see there is no hidden input, only `1` for the input checkbox tag, correctly.
### More custom values

### Example Checkbox with values `inactive` and `active` values:

TestForm class:
In this example we use two values for checked `active` value for checked and `inactive` value for unchecked.

```php
<?php
Expand Down Expand Up @@ -163,18 +155,18 @@ use Yiisoft\Form\Widget\Label;

/**
* @var FormModelInterface $data
* @var string $csrf
* @var object $csrf
*/
?>

<?= Form::widget()->action('widgets')->csrf($csrf)->begin(); ?>
<?= Checkbox::widget()->config($data, 'active', ['forceUncheckedValue' => 'inactive', 'value' => 'active'])->render(); ?>
<?= Checkbox::widget()->config($data, 'active', ['uncheckValue' => 'inactive', 'value' => 'active'])->render(); ?>
<hr class="mt-3">
<?= Field::widget()->submitButton(['class' => 'button is-block is-info is-fullwidth', 'value' => 'Save']); ?>
<?= Form::end(); ?>
```

### Code generated by `Checkbox` Widget
That would generate the following code:

```html
<form action="widgets" method="POST" _csrf="ifAHTGhruJd9LvvzfoU3xmftdLx3ReD89w21FZpDXajYum8dUAXb8jVEq8II1QGnVdwO9zsjl5bAfO1T_xlt3g==">
Expand All @@ -188,7 +180,6 @@ use Yiisoft\Form\Widget\Label;
</form>
```

As we can see in the code we have the value `inactive` for the hidden input and `active` for the input checkbox tag, correctly.

### `Checkbox` methods:

Expand All @@ -197,6 +188,8 @@ Method | Description | Default
`enclosedByLabel(bool $value = true)` | If the widget should be enclosed by label. | `true`
`label(string $value)` | The label text. | `''`
`labelAttributes(array $attributes = [])` | The HTML attributes for the label tag. | `[]`
`uncheckValue($value)` | The value that will be returned when the checkbox is not checked. | `0`
`value($value)` | The value that will be returned when the checkbox is checked. | `1`

### `Common` methods:

Expand All @@ -211,4 +204,3 @@ Method | Description | Default
`required(bool $value = true)` | Sets the required attribute | `false`
`readonly()` | Sets the readonly attribute | `false`
`tabIndex(int $value = 0)` | Sets the tabindex attribute | `0`

52 changes: 41 additions & 11 deletions src/Widget/Checkbox.php
Expand Up @@ -5,7 +5,7 @@
namespace Yiisoft\Form\Widget;

use InvalidArgumentException;
use Yiisoft\Arrays\ArrayHelper;
use Stringable;
use Yiisoft\Form\Helper\HtmlForm;
use Yiisoft\Form\Widget\Attribute\CommonAttributes;
use Yiisoft\Form\Widget\Attribute\ModelAttributes;
Expand All @@ -27,6 +27,10 @@ final class Checkbox extends Widget
private bool $enclosedByLabel = true;
private string $label = '';
private array $labelAttributes = [];
/** @var bool|float|int|string|Stringable|null */
private $uncheckValue = '0';
/** @var scalar|Stringable|null */
private $value = '1';

/**
* If the widget should be enclosed by label.
Expand Down Expand Up @@ -81,6 +85,34 @@ public function labelAttributes(array $attributes = []): self
return $new;
}

/**
* @param bool|float|int|string|Stringable|null $value Value that corresponds to "unchecked" state of the input.
*
* @return static
*/
public function uncheckValue($value): self
{
$new = clone $this;
$new->uncheckValue = is_bool($value) ? (int) $value : $value;
return $new;
}

/**
* The value of the radio button.
*
* @param scalar|Stringable|null $value
*
* @return static
*
* @link https://www.w3.org/TR/2012/WD-html-markup-20120329/input.checkbox.html#input.checkbox.attrs.value
*/
public function value($value): self
{
$new = clone $this;
$new->attributes['value'] = $value;
return $new;
}

/**
* @return string the generated checkbox tag.
*/
Expand All @@ -89,31 +121,29 @@ protected function run(): string
$new = clone $this;

$checkbox = CheckboxTag::tag();

$value = HtmlForm::getAttributeValue($new->getFormModel(), $new->attribute);

if (is_iterable($value) || is_object($value)) {
throw new InvalidArgumentException('Checkbox widget value can not be an iterable or an object.');
}

$new->attributes['value'] = array_key_exists('value', $new->attributes) ? $new->attributes['value'] : '1';

/** @var bool|float|int|string|null */
$forceUncheckedValue = ArrayHelper::remove($new->attributes, 'forceUncheckedValue', '0');

unset($new->attributes['forceUncheckedValue']);
/** @var scalar|Stringable|null */
$valueDefault = array_key_exists('value', $new->attributes) ? $new->attributes['value'] : $new->value;
$valueDefault = is_bool($valueDefault) ? (int) $valueDefault : $valueDefault;

if ($new->enclosedByLabel === true) {
$label = $new->label !== '' ? $new->label : HtmlForm::getAttributeLabel($new->getFormModel(), $new->attribute);
$label = $new->label !== '' ?
$new->label : HtmlForm::getAttributeLabel($new->getFormModel(), $new->attribute);
$checkbox = $checkbox->label($label, $new->labelAttributes);
}

return $checkbox
->checked("$value" === "{$new->attributes['value']}")
->checked("$value" === "$valueDefault")
->attributes($new->attributes)
->id($new->getId())
->name(HtmlForm::getInputName($new->getFormModel(), $new->attribute))
->uncheckValue($forceUncheckedValue)
->uncheckValue($new->uncheckValue)
->value($valueDefault)
->render();
}
}
16 changes: 12 additions & 4 deletions src/Widget/Field.php
Expand Up @@ -66,7 +66,7 @@ final class Field extends Widget
*
* @param array $attributes the tag attributes in terms of name-value pairs. The following options are specially
* handled:
* - `forceUncheckedValue`: string, the value associated with the uncheck state of the {@see Checkbox}.
* - `uncheckValue`: string, the value associated with the uncheck state of the {@see Checkbox}.
* This attribute will render a hidden input so that if the {@see Checkbox} is not checked and is submitted,
* the value of this attribute will still be submitted to the server via the hidden input. If you do not want any
* hidden input, you should explicitly no set.
Expand Down Expand Up @@ -96,13 +96,21 @@ public function checkbox(array $attributes = [], bool $enclosedByLabel = true):

if (isset($attributes['label']) && is_string($attributes['label'])) {
$checkbox = $checkbox->label($attributes['label']);
unset($attributes['label']);
}

if (isset($attributes['labelAttributes']) && is_array($attributes['labelAttributes'])) {
$checkbox = $checkbox->labelAttributes($attributes['labelAttributes']);
unset($attributes['labelAttributes']);
}

unset($attributes['label'], $attributes['labelAttributes']);
if (
isset($attributes['uncheckValue']) &&
((is_scalar($attributes['uncheckValue'])) || $attributes['uncheckValue'] instanceof Stringable)
) {
$checkbox = $checkbox->uncheckValue($attributes['uncheckValue']);
unset($attributes['uncheckValue']);
}

$new->parts['{input}'] = $checkbox
->config($new->getFormModel(), $new->attribute, $attributes)
Expand Down Expand Up @@ -502,7 +510,7 @@ public function password(array $attributes = []): self
*
* @param array $attributes the tag attributes in terms of name-value pairs. The following options are specially
* handled:
* - `forceUncheckedValue`: string, the value associated with the uncheck state of the {@see Radio}.
* - `uncheckValue`: string, the value associated with the uncheck state of the {@see Radio}.
* This attribute will render a hidden input so that if the {@see Radio} is not checked and is submitted,
* the value of this attribute will still be submitted to the server via the hidden input. If you do not want any
* hidden input, you should explicitly no set.
Expand Down Expand Up @@ -731,7 +739,7 @@ public function resetButton(array $attributes = []): self
*
* @param array $attributes the tag attributes in terms of name-value pairs. The following options are specially
* handled:
* - `forceUncheckedValue`: string, the value associated with the uncheck state of the {@see RadioList}.
* - `uncheckValue`: string, the value associated with the uncheck state of the {@see RadioList}.
* This attribute will render a hidden input so that if the {@see RadioList} is not checked and is submitted, the
* value of this attribute will still be submitted to the server via the hidden input. If you do not want any hidden
* input, you should explicitly no set.
Expand Down
41 changes: 26 additions & 15 deletions tests/Widget/CheckboxTest.php
Expand Up @@ -26,17 +26,6 @@ public function testEnclosedByLabel(): void
);
}

public function testForceUncheckedValue(): void
{
$expected = <<<'HTML'
<input type="hidden" name="TypeForm[bool]" value="0"><label><input type="checkbox" id="typeform-bool" name="TypeForm[bool]" value="1"> Bool</label>
HTML;
$this->assertSame(
$expected,
Checkbox::widget()->config($this->formModel, 'bool', ['forceUncheckedValue' => '0'])->render(),
);
}

public function testForm(): void
{
$expected = <<<'HTML'
Expand All @@ -55,13 +44,14 @@ public function testImmutability(): void
$this->assertNotSame($checkbox, $checkbox->form(''));
$this->assertNotSame($checkbox, $checkbox->label(''));
$this->assertNotSame($checkbox, $checkbox->labelAttributes([]));
$this->assertNotSame($checkbox, $checkbox->uncheckValue('0'));
$this->assertNotSame($checkbox, $checkbox->value('1'));
}

public function testLabelWithLabelAttributes(): void
{
$this->formModel->setAttribute('bool', true);
$expected = <<<'HTML'
<input type="hidden" name="TypeForm[bool]" value="0"><label class="test-class"><input type="checkbox" id="typeform-bool" name="TypeForm[bool]" value="1" checked> test-text-label</label>
<input type="hidden" name="TypeForm[bool]" value="0"><label class="test-class"><input type="checkbox" id="typeform-bool" name="TypeForm[bool]" value="1"> test-text-label</label>
HTML;
$html = Checkbox::widget()
->config($this->formModel, 'bool')
Expand All @@ -73,13 +63,34 @@ public function testLabelWithLabelAttributes(): void

public function testRender(): void
{
$this->formModel->setAttribute('bool', true);
$expected = <<<'HTML'
<input type="hidden" name="TypeForm[bool]" value="0"><label><input type="checkbox" id="typeform-bool" name="TypeForm[bool]" value="1" checked> Bool</label>
<input type="hidden" name="TypeForm[bool]" value="0"><label><input type="checkbox" id="typeform-bool" name="TypeForm[bool]" value="1"> Bool</label>
HTML;
$this->assertSame($expected, Checkbox::widget()->config($this->formModel, 'bool')->render());
}

public function testUncheckValue(): void
{
$expected = <<<'HTML'
<input type="hidden" name="TypeForm[bool]" value="0"><label><input type="checkbox" id="typeform-bool" name="TypeForm[bool]" value="1"> Bool</label>
HTML;
$this->assertSame(
$expected,
Checkbox::widget()->config($this->formModel, 'bool')->uncheckValue('0')->render(),
);
}

public function testValue(): void
{
$expected = <<<'HTML'
<input type="hidden" name="TypeForm[string]" value="inactive"><label><input type="checkbox" id="typeform-string" name="TypeForm[string]" value="active"> String</label>
HTML;
$this->assertSame(
$expected,
Checkbox::widget()->config($this->formModel, 'string')->uncheckValue('inactive')->value('active')->render(),
);
}

public function testValues(): void
{
// value bool false
Expand Down

0 comments on commit 0f1397b

Please sign in to comment.