Symfony Choice type with disabled options - forms

Is there any way with Symfony to render a <select> form type with disabled options, based on the truthyness of the given choices options ?
I saw this thread (thanks to DonCallisto) about disabling choice expanded options ;
However I do not want to have an expanded choice.
I would like to keep a select element, with disabled options.
$builder->add('list', 'choice', array(
'choices' => array(
array(
'value' => 1,
'label' => '1',
'disabled' => false
),
array(
'value' => 2,
'label' => '2',
'disabled' => false
),
array(
'value' => 3,
'label' => '3',
'disabled' => true
)
),
// Instead of
// 'choices' => array(
// 1 => 'Option 1',
// 2 => 'Option 2',
// 3 => 'Option 3'
// )
);
# Which would render to the following element
<select [...]>
<option value='1'>1</value>
<option value='2'>2</value>
<option value='3' disabled='disabled'>3</value>
</select>
I just can't find the way...
Is it necessary to build its own field type ?

Since version 2.7, Symfony has introduced a way to set choice attributes using a callable, this is just what you need.
this code is taken from official Symfony documentation
$builder->add('attending', ChoiceType::class, array(
'choices' => array(
'Yes' => true,
'No' => false,
'Maybe' => null,
),
'choices_as_values' => true,
'choice_attr' => function($val, $key, $index) {
// adds a class like attending_yes, attending_no, etc
return ['class' => 'attending_'.strtolower($key)];
},
));
you can use the 'choice_attr' and pass a function that will decide wether to add a disabled attribute or not depending on the value, key or index of the choice.
...
'choice_attr' => function($key, $val, $index) {
$disabled = false;
// set disabled to true based on the value, key or index of the choice...
return $disabled ? ['disabled' => 'disabled'] : [];
},
...

According to the forms layout :
https://github.com/symfony/symfony/blob/2.7/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
and the choice_widget_collapsed and choice_widget_options,i don't think it's possible directly with the default Choice form of Symfony.
You can try :
to build your own Choice form (by extending the existing one and adding parameters to option list, i think it's the best way)
to use javascript/jquery to modify the options parameters at load time with the existing Symfony's Choice from

This is another way to load options from Entity.
You can create a new Entity Attending and use it with EntityType.
->add('attending', EntityType::class, [
'class' => Attending::class,
'choice_attr' => function($key, $val, $index) {
if ($key->getId() == StaticVal) {
return ['disabled' => 'disabled'];
}
return [];
}]
Here StaticVal can be any value that you want to disable it.
$key will give you an object value
$val will give you a value starting from 0 if you are using the placeholder option.

Related

Radio button: input was not found in the haystack?

Whenever I submit the form I get this message:
The input was not found in the haystack.
This is for the shipping-method element (radio button). Can't figure out what it means, the POST data for that element is not null.
public function getInputFilter()
{
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
// Some other basic filters
$inputFilter->add(array(
'name' => 'shipping-method',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim')
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'max' => 20,
),
),
array(
'name' => 'Db\RecordExists',
'options' => array(
'table' => 'shipping',
'field' => 'shipping_method',
'adapter' => $this->dbAdapter
)
),
),
));
$inputFilter->get('shipping-address-2')->setRequired(false);
$inputFilter->get('shipping-address-3')->setRequired(false);
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
I only keep finding solutions for <select>.
Here's the sample POST data:
object(Zend\Stdlib\Parameters)#143 (1) {
["storage":"ArrayObject":private] => array(9) {
["shipping-name"] => string(4) "TEST"
["shipping-address-1"] => string(4) "test"
["shipping-address-2"] => string(0) ""
["shipping-address-3"] => string(0) ""
["shipping-city"] => string(4) "TEST"
["shipping-state"] => string(4) "TEST"
["shipping-country"] => string(4) "TEST"
["shipping-method"] => string(6) "Ground"
["submit-cart-shipping"] => string(0) ""
}
}
UPDATE:
form.phtml
<div class="form-group">
<?= $this->formRow($form->get('shipping-method')); ?>
<?= $this->formRadio($form->get('shipping-method')
->setValueOptions(array(
'Ground' => 'Ground',
'Expedited' => 'Expedited'))
->setDisableInArrayValidator(true)); ?>
</div>
ShippingForm.php
$this->add(array(
'name' => 'shipping-method',
'type' => 'Zend\Form\Element\Radio',
'options' => array(
'label' => 'Shipping Method',
'label_attributes' => array(
'class' => 'lbl-shipping-method'
),
)
));
The problem lies with when you use the setValueOptions() and the setDisableInArrayValidator(). You should do this earlier within your code as it is never set before validating your form and so the inputfilter still contain the defaults as the InArray validator. As after validation, which checks the inputfilter, you set different options for the shipping_methods.
You should move the setValueOptions() and the setDisableInArrayValidator() before the $form->isValid(). Either by setting the right options within the form itsself or doing this in the controller. Best way is to keep all of the options in one place and doing it inside the form class.
$this->add([
'name' => 'shipping-method',
'type' => 'Zend\Form\Element\Radio',
'options' => [
'value_options' => [
'Ground' => 'Ground',
'Expedited' => 'Expedited'
],
'disable_inarray_validator' => true,
'label' => 'Shipping Method',
'label_attributes' => [
'class' => 'lbl-shipping-method',
],
],
]);
Another small detail you might want to change is setting the value options. They are now hardcoded but your inputfilter is checking against database records whether they exist or not. Populate the value options with the database records. If the code still contains old methods but the database has a few new ones, they are not in sync.
class ShippingForm extends Form
{
private $dbAdapter;
public function __construct(AdapterInterface $dbAdapter, $name = 'shipping-form', $options = [])
{
parent::__construct($name, $options)
// inject the databaseAdapter into your form
$this->dbAdapter = $dbAdapter;
}
public function init()
{
// adding form elements to the form
// we use the init method to add form elements as from this point
// we also have access to custom form elements which the constructor doesn't
$this->add([
'name' => 'shipping-method',
'type' => 'Zend\Form\Element\Radio',
'options' => [
'value_options' => $this->getDbValueOptions(),
'disable_inarray_validator' => true,
'label' => 'Shipping Method',
'label_attributes' => [
'class' => 'lbl-shipping-method',
],
],
]);
}
private function getDbValueOptions()
{
$statement = $this->dbAdapter->query('SELECT shipping_method FROM shipping');
$rows = $statement->execute();
$valueOptions = [];
foreach ($rows as $row) {
$valueOptions[$row['shipping_method']] = $row['shipping_method'];
}
return $valueOptions;
}
}
Just had this happen yesterday.
The select and multi select ZF2+ elements have a built in in_array validator.
Remember filters occur before validators.
You may be doing too much here -- it is very rare to need to filter or add validators ot select and multi select form elements in ZF2 forms. The built in element validator is robust, ZF does a lot of work for us.
Try removing both filter and validator for the element, such as:
$inputFilter->add(array(
'name' => 'shipping-method',
'required' => true,
));
There is another edge case that I have seen: changing the select element's valueOptions somewhere in the controller (or view) resulting in different valueOptions used in view vs form validation (in our case it was replacing the element with a new one before validation).
I think your problem lies in the fact you are adding your value options after the InArray validator has been set, hence the validator has no haystack.
Try this
$this->add(array(
'name' => 'shipping-method',
'type' => 'Zend\Form\Element\Radio',
'options' => array(
'label' => 'Shipping Method',
'label_attributes' => array(
'class' => 'lbl-shipping-method'
),
'value_options' => array(
'Ground' => 'Ground',
'Expedited' => 'Expedited'
),
'disable_inarray_validator' => TRUE,
)
));
and remove setValueOptions and setDisableInArrayValidator from your view.
Hope this works.

Create a SelectBox using Form Helper in CakePHP3

I am trying to make a combo box for an edit page.
echo $this->Form->select('status',
['empty' => 'Select Status'],
['class' => 'form-control', 'required']
);
Here I want to add 2 things :
$options = array('0' => 'Inactive',
'1' => 'Active',
);
and selected value. suppose that is $status;
I tried with different options but sometime it do not add classes and sometime it shows options in tag
It will be great if somebody give clue.
Thanks
<?= $this->Form->input('status', [
'type' => 'select',
'options' => ['0' => __('Inactive') , '1' => __('Active')],
'empty' => __('Select Status'),
'class' => 'form-control',
'required' => true, 'label' => __('Type')
])
?>

Zend Framework 2 Radio Button null value default

I am using Zend Framework 2, Doctrine Module and SQLServer to build a number of products.
I have a question in relation to Zend\Form\Radio.
I have the following defined in a form:
// boolean $disabled_access
$this->add(array(
'name' => 'disabled_access',
'type' => 'radio',
'options' => array(
'label' => 'Disabled Access',
'value_options' => array('1'=>"Yes", '0' => 'No'),
'allow_empty' => true,
'nullable' => true,
),
'attributes' => array('value' => null),
));
It is bound to a $building entity.
If the value of 'disabled_access' is set to true in the DB, the radio button renders correctly. Similarly if it's is set to false.
However, if the column has a NULL value, the radio button defaults to 'No'. How do I set it up to show all three potential results?
This should be enough -
$this->add(array(
'name' => 'disabled_access',
'type' => 'radio',
'options' => array(
'label' => 'Disabled Access',
'value_options' => array('1'=>'Yes', '0' => 'No'),
),
)
);
If value in database is -
1 -> 'Yes' radio button will be selected,
0 -> 'No' radio button will be selected,
NULL -> none of them will be selected.
Its working for me.
I hope it helps.

How to validate a checkbox in ZF2

I've read numerous workarounds for Zend Framework's lack of default checkbox validation.
I have recently started using ZF2 and the documentation is a bit lacking out there.
Can someone please demonstrate how I can validate a checkbox to ensure it was ticked, using the Zend Form and Validation mechanism? I'm using the array configuration for my Forms (using the default set-up found in the example app on the ZF website).
Try this
Form element :
$this->add(array(
'type' => 'Zend\Form\Element\Checkbox',
'name' => 'agreeterms',
'options' => array(
'label' => 'I agree to all terms and conditions',
'use_hidden_element' => true,
'checked_value' => 1,
'unchecked_value' => 'no'
),
));
In filters, add digit validation
use Zend\Validator\Digits; // at top
$inputFilter->add($factory->createInput(array(
'name' => 'agreeterms',
'validators' => array(
array(
'name' => 'Digits',
'break_chain_on_failure' => true,
'options' => array(
'messages' => array(
Digits::NOT_DIGITS => 'You must agree to the terms of use.',
),
),
),
),
)));
You could also just drop the hidden form field (which I find a bit weird from a purist HTML point of view) from the options instead of setting its value to 'no' like this:
$this->add(array(
'type' => 'Zend\Form\Element\Checkbox',
'name' => 'agreeterms',
'options' => array(
'label' => 'I agree to all terms and conditions',
'use_hidden_element' => false
),
));
I had the same problem and did something similar to Optimus Crew's suggestion but used the Identical Validator.
If you don't set the checked_value option of the checkbox and leave it as the default it should pass in a '1' when the data is POSTed. You can set it if you require, but make sure you're checking for the same value in the token option of the validator.
$this->filter->add(array(
'name' => 'agreeterms',
'validators' => array(
array(
'name' => 'Identical',
'options' => array(
'token' => '1',
'messages' => array(
Identical::NOT_SAME => 'You must agree to the terms of use.',
),
),
),
),
));
This won't work if you use the option 'use_hidden_element' => false for the checkbox for the form. If you do this, you'll end up displaying the default NotEmpty message Value is required and can't be empty
This isn't directly related to the question, but here's some zf2 checkbox tips if you're looking to store a user's response in the database...
DO use '1' and '0' strings, don't bother trying to get anything else to work. Plus, you can use those values directly as SQL values for a bit/boolean column.
DO use hidden elements. If you don't, no value will get posted with the form and no one wants that.
DO NOT try to filter the value to a boolean. For some reason, when the boolean value comes out to be false, the form doesn't validate despite having 'required' => false;
Example element creation in form:
$this->add([
'name' => 'cellPhoneHasWhatsApp',
'type' => 'Checkbox',
'options' => [
'label' => 'Cell phone has WhatsApp?',
'checked_value' => '1',
'unchecked_value' => '0',
'use_hidden_element' => true,
],
]);
Example input filter spec:
[
'cellPhoneHasWhatsApp' => [
'required' => false,
],
]
And here's an example if you want to hide some other form fields using bootstrap:
$this->add([
'name' => 'automaticTitle',
'type' => 'Checkbox',
'options' => [
'label' => 'Automatically generate title',
'checked_value' => '1',
'unchecked_value' => '0',
'use_hidden_element' => true,
],
'attributes' => [
'data-toggle' => 'collapse',
'data-target' => '#titleGroup',
'aria-expanded' => 'false',
'aria-controls' => 'titleGroup'
],
]);
I'm a ZF2 fan, but at the end of the day, you just have to find out what works with it and what doesn't (especially with Forms). Hope this helps somebody!
Very old question, but figured it might still be used/referenced by people, like me, still using Zend Framework 2. (Using ZF 2.5.3 in my case)
Jeff's answer above helped me out getting the right config here for what I'm using. In my use case I require the Checkbox, though leaving it empty will count as a 'false' value, which is allowed. His answer helped me allow the false values, especially his:
DO NOT try to filter the value to a boolean. For some reason, when the boolean value comes out to be false
The use case is to enable/disable certain entities, such as Countries or Languages so they won't show up in a getEnabled[...]() Repository function.
Form element
$this->add([
'name' => 'enabled',
'required' => true,
'type' => Checkbox::class,
'options' => [
'label' => _('Enabled'),
'label_attributes' => [
'class' => '',
],
'use_hidden_element' => true,
'checked_value' => 1,
'unchecked_value' => 0,
],
'attributes' => [
'id' => '',
'class' => '',
],
]);
Input filter
$this->add([
'name' => 'enabled',
'required' => true,
'validators' => [
[
'name' => InArray::class,
'options' => [
'haystack' => [true, false],
],
],
],
])

Zend: Form validation: value was not found in the haystack error

I have a form with 2 selects. Based on the value of the first select, it updates the values of the second select using AJAX. Doing this makes the form not being valid. So, I made the next change:
$form=$this->getAddTaskForm(); //the form
if(!$form->isValid($_POST)) {
$values=$form->getValues();
//get the options and put them in $options
$assignMilestone=$form->getElement('assignedMilestone');
$assignMilestone->addMultiOptions($options);
}
if($form->isValid($_POST)) {
//save in the database
}else {
//redisplay the form
}
Basically, I check if it is valid and it isn't if the user changed the value of the first select. I get the options that populated the second select and populate the form with them. Then I try to validate it again. However this doesn't work. Anybody can explain why? The same "value was not found in the haystack" is present.
You could try to deactivate the validator:
in your Form.php
$field = $this->createElement('select', 'fieldname');
$field->setLabel('Second SELECT');
$field->setRegisterInArrayValidator(false);
$this->addElement($field);
The third line will deactivate the validator and it should work.
You can also disable the InArray validator using 'disable_inarray_validator' => true:
For example:
$this->add( array(
'name' => 'progressStatus',
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'options' => array(
'disable_inarray_validator' => true,
),
));
Additionaly you should add you own InArray Validator in order to protect your db etc.
In Zend Framework 1 it looks like this:
$this->addElement('select', $name, array(
'required' => true,
'label' => 'Choose sth:',
'filters' => array('StringTrim', 'StripTags'),
'multiOptions' => $nestedArrayOptions,
'validators' => array(
array(
'InArray', true, array(
'haystack' => $flatArrayOptionsKeys,
'messages' => array(
Zend_Validate_InArray::NOT_IN_ARRAY => "Value not found"
)
)
)
)
));
Where $nestedArrayOptions is you multiOptions and $flatArrayOptionsKeys contains you all keys.
You may also add options to select element before checking for the form validation. This way you are insured the select value is in range.