Disabling some checkboxes of choice widget in buildForm() - symfony

I have a form widget of type "choice" which is being displayed as a list of many checkboxes. Everything works great. So to stress it out: there is ONE widget, with MANY checkboxes (and NOT several checkbox widgets).
Now, I want to disable some of those checkboxes. The data for that is avaliable in the $options-Array.
Here is the buildForm()-function of my FooType.php
...
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('foo', 'choice', array('choices' => $options['choiceArray']['id'],
'multiple' => true,
'expanded' => true,
'disabled' => $options['choiceArray']['disabled'] // does not work (needs a boolean)
'data' => $options['choiceArray']['checked'], // works
'attr' => array('class' => 'checkbox')))
;
}
...
My Twig-template looks like this:
{% for foo in fooForm %}
<dd>{{ form_widget(foo) }}</dd>
{% endfor %}
I can only disable ALL the checkboxes (by setting 'disabled' => true in buildForm). And passing an array there does not work (as commented in the snippet).
How can I disable some selected checkboxed (stored in $options['choiceArray']['disabled']) of my choice widget?

I have solved the problem using JQuery.
in my FooType.php I stringify the Array of fields that should be disabled.
I pass that string in the buildForm()-Function via a hidden field to the template
there I use JQuery to split the string again into IDs and process disable the checkboxes and grey out the label
Here is the PHP-Code (FooType.php):
...
public function buildForm(FormBuilderInterface $builder, array $options)
{
$disabledCount = sizeof($options['choiceArray']['disabled']);
$disabledString = '';
for ($i = 0; $i < $disabledCount; $i++)
{
$disabledString .= $options['choiceArray']['disabled'][$i];
if ($i < $disabledCount-1)
{
$disabledString .= '|';
}
}
$builder
->add('foo', 'choice', array('choices' => $options['choiceArray']['id'],
'multiple' => true,
'expanded' => true,
'data' => $options['choiceArray']['checked'],
'attr' => array('class' => 'checkbox')))
->add('foo_disabled', 'hidden', array('data' => $disabledString))
;
}
...
Here is the JavaScript part (Twig-template):
function disableModule()
{
var disabledString = $('#foo_disabled').val();
var disabledArray = disabledString.split('|');
$.each( disabledArray, function( disKey, disVal )
{
// deactivate checkboxes
$('input[id="'+idCurrent+'"]').attr("disabled", true);
// grey out label for checkboxes
$('label[for="'+idCurrent+'"]').attr("style", "color: gray;");
});
}
In my Entity/Foo.php I had to add the property "foo_disabled" of type string with setter and getter methods.

This page is first on Goole search results for 'twig checkbox checked and disabled'
To set some checkbox inputs checked or/and disabled in twig, one could use choice_attr
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('email', EmailType::class, ['label'=>'Login: '])
->add('roles', ChoiceType::class, [
'label'=>'Role: ',
'multiple'=>true,
'expanded'=>true,
'choices'=>['User'=>'ROLE_USER','Admin'=>'ROLE_ADMIN'],
'choice_attr'=> [
'User' => ['disabled'=>'disabled', 'checked'=>'checked'],
]
])
->add('password', PasswordType::class, ['label'=>'Password: '])
->add('register', SubmitType::class, ['label' => 'Register'])
;
}
In this example I set checked and disabled checkbox for ROLE_USER as this is default role.

Related

Symfony Form - Add required fields to form depending on other field value

I want to add some required fields to my form when an other field has some value. I've tried to do it with PRE_SET_DATA event but I cannot get data in my event.
My example here is to add partner name field when a user is married.
My UserType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('familyStatus', ChoiceType::class, [
'label' => 'Statut de famille',
'label_attr' => [
'class' => 'fg-label'
],
'attr' => [
'class' => 'sc-gqjmRU fQXahQ'
],
'required' => true,
'choices' => [
'Married' => 'M',
'Single' => 'S'
]
])
->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$form = $event->getForm();
$user = $event->getData();
$status = $user->getFamilyStatus(); // Give me NULL
//$status = $form->get('familyStatus')->getData() Give me NULL too
/*
if ($user && $status === 'M') {
$form->add('partnerName', TextType::class, [
'required' => true,
'mapped' => false
]);
)
*/
})
;
}
What's wrong ? How can I add dynamically new fields depending on other field ?
I also tried with POST_SET_DATA but it's not working.
You need the second example from this part of the docs link.
Basically you set the event listener to the entire form. You should add another listener to the field itself with POST_SUBMIT event.

symfony3 - Showing 'This value is not valid' for choice type of dropdown that options adding via ajax

My scenario is,
I need to create a entity(module) with display order and the module is under a topic entity and association made correctly. On form load the topic dropdown and display order dropdown will be blank along the module name. When selecting topic the display order will fill with options via ajax/js. Display order will be 1 to a number that will be the total modules under the specific topic+1 . The upcoming display order will be selected automatically. And that's working perfectly. But my issue is about the display order validation after submit. Its saying 'This value is not valid'. I understands this is due to not giving 'choices' as array in form type, but this case i cant give as static in form type. Please help anyone knows a solution.
class ModuleType extends AbstractType {
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('topic', EntityType::class, [
'class' => 'AppBundle:Topic',
'choice_label' => 'name',
'placeholder' => 'Choose a Topic'
])
->add('name')
->add('description', TextareaType::class)
->add('displayOrder', ChoiceType::class)
->add('save', SubmitType::class, [
'attr' => ['class' => 'form-control button btn-sm nomargin']
])
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Module'
));
}
}
Try to use the Event Listener.
In your case for exemple:
// TopicType.php
private function addEventListener(FormBuilderInterface $builder)
{
// this function permit to valid values of topics
$annonymFunction = function(FormInterface $form, $diplayOrder) {
$entities = $this->container->get('doctrine.orm.default_entity_manager')
->getRepository('YourBundle:Topic')
->findAll();
if ($entities) {
$topics = array();
foreach($topics as $topic) {
$topics[$topic->getName()] = $topic->getName();
}
}
else $topics = null;
$form->add('topic', EntityType::class, array(
'attr' => array('class' => 'topic'),
'choices' => $topics));
};
$builder
->get('displayOrder')
->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) use ($annonymFunction) {
$annonymFunction($event->getForm()->getParent(), $event->getForm()->getData());
});
}
Hope to help.

How to set select option disabled in formtype disabled dynamically?

I have a form type in symfony2.5 that has choice type which has many options set dynamically as
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('service_id', 'choice', array(
'label' => 'Service',
'choices' => $this->getChoice()
));
.......
.......
}
protected function getChoice()
{
$choices = array();
$options = array(
'1' => 'test1',
.............
);
foreach($options as $key => $option)
{
if($key%2 == 0) {
....
} else {
////disabled choice
}
}
}
How can i set this choices set disabled??
This can be done using form event listeners and finishView method. Similar question has been answered before here - How to disable specific item in form choice type?
Note that PRE_BIND event is deprecated. Use PRE_SUBMIT instead.
Quote:
The events PRE_SUBMIT, SUBMIT and POST_SUBMIT were introduced in
Symfony 2.3. Before, they were named PRE_BIND, BIND and POST_BIND.
http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html

Build a form having a checkbox for each entity in a doctrine collection

I'm displaying an html table for a filtered collection of entities and I want to display a checkbox in each row as part of a form which will add the selected entities to a session var.
I'm thinking that each checkbox should have the entity id as its value and I'll get an array of ids from the form field data (ok, so the value ought to be an indirect ref to the entity, but for the sake of simplicity).
I've tried creating a form Type with a single entity type field, mapped to the id property of the entity and embedded into another form Type which has a collection type field.
class FooEntitySelectByIdentityType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('foo_id', 'entity', array(
'required' => false,
'class' => 'MeMyBundle:FooEntity',
'property' => 'id',
'multiple' => true,
'expanded' => true
));
}
# ...
and
class FooEntitySelectionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('identity', 'collection', array(
'type' => new FooEntitySelectByIdentityType,
'options' => array(
'required' => false,
'multiple' => true,
'expanded' => true,
'attr' => array('class' => 'foo')
),
));
}
# ...
and in a controller the form is created with a collection of entities as the initial data
$form = $this
->createForm(
new \Me\MyBundle\Form\Type\FooEntitySelectionType,
$collection_of_foo
)
->createView()
;
When the form is rendered there is a single label for the identity field, but no widgets.
Is it even possible to use entity and collection type fields in this particular way?
If so, what might I be doing wrong?
I think this will answer your question.
Forget the FooEntitySelectionType. Add a property_path field option to FooEntitySelectByIdentityType and set the data_class option to null:
class FooEntitySelectByIdentityType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('foo_id', 'entity', array(
'required' => false,
'class' => 'MeMyBundle:FooEntity',
'property' => 'id',
'property_path' => '[id]', # in square brackets!
'multiple' => true,
'expanded' => true
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => null,
'csrf_protection' => false
));
}
# ...
and in your controller, build the FooEntitySelectByIdentityType:
$form = $this
->createForm(
new \Me\MyBundle\Form\Type\FooEntitySelectByIdentityType,
$collection_of_foo
)
->createView()
;
and then in the controller action which receives the POSTed data:
$form = $this
->createForm(new \Me\MyBundle\Form\Type\FooEntitySelectByIdentityType)
;
$form->bind($request);
if ($form->isValid()) {
$data = $form->getData();
$ids = array();
foreach ($data['foo_id'] as $entity) {
$ids[] = $entity->getId();
}
$request->getSession()->set('admin/foo_list/batch', $ids);
}
and finally, in your twig template:
{# ... #}
{% for entity in foo_entity_collection %}
{# ... #}
{{ form_widget(form.foo_id[entity.id]) }}
{# ... #}
If someone is looking for solution for Symfony >=2.3
You have to change this:
$data = $form->getData();
$ids = array();
foreach ($data['foo_id'] as $entity) {
$ids[] = $entity->getId();
}
to this:
$data = $form['foo_id']->getData();
$ids = array();
foreach ($data as $entity) {
$ids[] = $entity->getId();
}
I create a symfony bundle for render a collection of entities as a checkbox table in a configurable way. Although it has been a while since this question was asked, I hope this can help others with this same kind of problem.

Validating dynamically loaded choices in Symfony 2

I have a choice field type named *sub_choice* in my form whose choices will be dynamically loaded through AJAX depending on the selected value of the parent choice field, named *parent_choice*. Loading the choices works perfectly but I'm encountering a problem when validating the value of the sub_choice upon submission. It gives a "This value is not valid" validation error since the submitted value is not in the choices of the sub_choice field when it was built. So is there a way I can properly validate the submitted value of the sub_choice field? Below is the code for building my form. I'm using Symfony 2.1.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('parent_choice', 'entity', array(
'label' => 'Parent Choice',
'class' => 'Acme\TestBundle\Entity\ParentChoice'
));
$builder->add('sub_choice', 'choice', array(
'label' => 'Sub Choice',
'choices' => array(),
'virtual' => true
));
}
To do the trick you need to overwrite the sub_choice field before submitting the form:
public function buildForm(FormBuilderInterface $builder, array $options)
{
...
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$parentChoice = $event->getData();
$subChoices = $this->getValidChoicesFor($parentChoice);
$event->getForm()->add('sub_choice', 'choice', [
'label' => 'Sub Choice',
'choices' => $subChoices,
]);
});
}
this accept any value
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$data = $event->getData();
if(is_array($data['tags']))$data=array_flip($data['tags']);
else $data = array();
$event->getForm()->add('tags', 'tag', [
'label' => 'Sub Choice',
'choices' => $data,
'mapped'=>false,
'required'=>false,
'multiple'=>true,
]);
});
Adding an alternate approach for future readers since I had to do a lot of investigation to get my form working. Here is the breakdown:
Adding a "New" option to a dropdown via jquery
If "New" is selected display new form field "Custom Option"
Submit Form
Validate data
Save to database
jquery code for twig:
$(function(){
$(document).ready(function() {
$("[name*='[custom_option]']").parent().parent().hide(); // hide on load
$("[name*='[options]']").append('<option value="new">New</option>'); // add "New" option
$("[name*='[options]']").trigger("chosen:updated");
});
$("[name*='[options]']").change(function() {
var companyGroup = $("[name*='[options]']").val();
if (companyGroup == 'new') { // when new option is selected display text box to enter custom option
$("[name*='[custom_option]']").parent().parent().show();
} else {
$("[name*='[custom_option]']").parent().parent().hide();
}
});
});
// Here's my Symfony 2.6 form code:
->add('options', 'entity', [
'class' => 'Acme\TestBundle\Entity\Options',
'property' => 'display',
'empty_value' => 'Select an Option',
'mapped' => true,
'property_path' => 'options.optionGroup',
'required' => true,
])
->add('custom_option', 'text', [
'required' => false,
'mapped' => false,
])
To handle the form data we need to use the PRE_SUBMIT form event.
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$data = $event->getData();
$form = $event->getForm();
if (isset($data['options']) && $data['options'] === 'new') {
$customOption = $data['custom_option'];
// todo: handle this better on your own
if (empty($customOption)) {
$form->addError(new FormError('Please provide a custom option'));
return;
}
// Check for a duplicate option
$matches = $this->doctrine->getRepository('Acme\TestBundle\Entity\Options')->matchByName([$customOption]);
if (count($matches) > 0) {
$form->addError(new FormError('Duplicate option found'));
return;
}
// More validation can be added here
// Creates new option in DB
$newOption = $this->optionsService->createOption($customOption); // return object after persist and flush in service
$data['options'] = $newOption->getOptionId();
$event->setData($data);
}
});
Let me know if ya'll have any questions or concerns. I know this might not be the best solution but it works. Thanks!
you cannot not build the sub_choice validation because during you config its validator you don't know which values are valid (values depend on value of parent_choice).
What you can do is to resolve parent_choice into entity before you make new YourFormType() in your controller.
Then you can get all the possible values for sub_choice and provide them over the form constructor - new YourFormType($subChoice).
In YourFormType you have to add __construct method like this one:
/**
* #var array
*/
protected $subChoice = array();
public function __construct(array $subChoice)
{
$this->subChoice = $subChoice;
}
and use provided values in form add:
$builder->add('sub_choice', 'choice', array(
'label' => 'Sub Choice',
'choices' => $this->subChoice,
'virtual' => true
));
Suppose for sub choices you have id's right ?
Create and empty array with a certain number of values and give it as a choice
$indexedArray = [];
for ($i=0; $i<999; $i++){
$indexedArray[$i]= '';
}
then 'choices' => $indexedArray, :)

Resources