How can I add css classes to specific symfony2 form choices? - symfony

I could do this with Javascript, but I was wondering if I could add a css class to specific symfony2 form choices (not the choice field itself, but the individual choices).
For example I want to apply different css styles to individual 'option' tags inside a 'select'. I could only find a way to add a class to the tag.
Thanks in advance.

I think you can simply do:
{{ form_widget(form.name, { 'attr' : { 'class' : 'myClass' } }) }}
... as explained here, without creating your own form style.

You can override the layout of specific widgets in your form, which means you can override the way the select renders and put in custom code to check what the value of the option is and output your custom class there.
You need to apply a custom layout to your form, like so
{% form_theme form 'form_theme.html.twig' %}
Then inside the layout file you need to override the specific field for that specific form (unless of course you want to edit the choice_widget directly in which case all fields that use choice will have the functionality).
To do that you have to copy the widget, so choice_widget, then name it [_formName_fieldName_widget]
So if your form name was events and your field name was requireTickets, it'd be _events_requireTickets_widget

The answers that were already provided are very good, and I think #CriticalImpact's is the most flexible. But I just wanted to mention that if you're using a form class, you can also add extra attributes to the field via the form builder definition itself:
class SomeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('someField', "text", array("attr" => array("class" => "someCssClass")))
->add("save", "submit");
}
}
I've found this helpful for basic forms, because it still allows you to make a simple {{ form(someForm) }} call in your Twig file.
(Note that this solution still has the drawback that #CriticalImpact mentioned above)

Add attributes like CSS styles to individual choices can nowadays be achieved with choice_attr, e.g.:
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
// ...
$builder->add('attending', ChoiceType::class, array(
'choices' => array(
'Yes' => true,
'No' => false,
'Maybe' => null,
),
'choice_attr' => function($val, $key, $index) {
// adds a class like attending_yes, attending_no, etc
return ['class' => 'attending_'.strtolower($key)];
},
));

Related

Use the category node name as a label in Zikula

I do use Zikula 1.5.2dev
My module is generated with modulestudio
I have made two entries in the Category registry. One is showing at the node "Global" and one at the node "Type"
In Global are several entries I can select. Some other entries are inside Type.
The selection is working in my template like expected. But how can I use the node names as a label?
I can not figure out in which template I have to place to label (have to do more searching). But more important, I do not know the right twig syntax to catch the categories label.
if you assign a category to the template, the properties are accessible like normal class properties.
{{ category.name }}
if you need the display name, this is stored as an array with lang codes as keys
{{ category.display_name['de'] }}
Hope that helps.
That sounds good. But now I have recognized this label seem not to be placed in a pure template. There is a form type defined:
class ShowRoomItemType extends AbstractShowRoomItemType
{
/**
* #inheritDoc
*/
public function addCategoriesField(FormBuilderInterface $builder, array $options)
{
$builder->add('categories', CategoriesType::class, [
'label' => $this->__('Category') . ':',
'empty_data' => null,
'attr' => [
'class' => 'category-selector'
],
'required' => false,
'multiple' => false,
'module' => 'RKShowRoomModule',
'entity' => 'ShowRoomItemEntity',
'entityCategoryClass' => 'RK\ShowRoomModule\Entity\ShowRoomItemCategoryEntity',
// added:
'includeGrandChildren' => true
]);
}
}
In my template it is called like this:
{{ form_row(quickNavForm.categories) }}
For this my skills are very limmited. I will write a feature request at modulestudio. (https://github.com/Guite/MostGenerator/issues/1147)
But big thanks for your reply!
This has been fixed for core 1.5.4 / 2.0.4 in https://github.com/zikula/core/pull/3846

Sonata how make autocomplete filter on current entity property

I'm working on a sf2 project using Sonata Admin Bundle.
The project is a Donations website for humanitarian mission.
I have a 'Personne' entity, represent benefactors (donation makers).
My problem is the following:
I have to filter results in the sonata list view using autocompletion.
I want filter results using the 'name' property of the current entity ('Personne').
What I'm expecting :
$datagridMapper
->add('personne', 'doctrine_orm_model_autocomplete',
array('label' => 'AutoComplete'),
null,
array('property' => 'name'))
// error output : " The option `association_mapping` must be set for field: `personne` "
You can see my full admin class and entity on this gist :
https://gist.github.com/chalasr/0658a02b1c04180f5563
I understand this field type is reserved to entity associations (by example I already use it for filter results of my Donation entity by Personne name (other admin class).
My question is :
Is it possible to do what I need ?
If I can't do that using this field type, what is the right way to achieve this task ?
Thank's for your help.
After a lot of tests, it seems this functionality isn't yet provided by Sonata.
So, I had build an homemade autocomplete method in my admin controller and used it as ajax in my overridden CRUD:list.html.twig template.
This method takes field name, other autocomplete fields values, and keyword as parameters and reload results on keyup event.
You can look on this gist:
https://gist.github.com/chalasr/5c27ae64dc596967f18a
If you have an idea/proposition for optimize my code (simple autocomplete field type for $formMapper ?), I'm really interested.
Well it's possible to make a choice field and use integrated select2 to handle autocomplete, I don't know how well it would fare with large tables though.
$datagridMapper
->add('personne','doctrine_orm_callback', array(
'callback' => array($this, 'filterByName'),
'field_type' => 'text',
), 'entity',array(
'class' => 'AppBundle\Entity\Personne',
'choice_label' => 'name'
))
We add 'doctrine_orm_callback' because regular string filter can't handle EntityType Field, so we need to do it ourselves.
public function filterByName($queryBuilder, $alias, $field, $value)
{
if (!$value['value']) {
return;
}
$queryBuilder
->andWhere($alias . '.name' . ' = ' . ':name' )
->setParameter('name' , $value['value']->getName());
return true;
}
1 more thing, select2 won't create autocomplete (search) box if dropdown box has less than 10 choices, because it's set up in Admin.js that way.
select.select2({
width: function(){
// Select2 v3 and v4 BC. If window.Select2 is defined, then the v3 is installed.
// NEXT_MAJOR: Remove Select2 v3 support.
return Admin.get_select2_width(window.Select2 ? this.element : jQuery(this));
},
dropdownAutoWidth: true,
minimumResultsForSearch: 10,
allowClear: allowClearEnabled
});
so you need to override it, if you want that to be less.
Fonctionnality is now bundled with Sonata as simple as:
$filter->add('label', ModelFilter::class, ['field_type'=>ModelAutocompleteType::class,'field_options' => ['property' => 'name']])

Symfony2 personnalisation every attr radio buttons

I can not find the opportunity to customize the attributes on radio buttons separately, because I want to insert a title tag and data-toggle="tooltip" to display a tooltip on every radio buttons
$builder
->add('type', 'choice', array(
'choices' => array(
'0' => 'Demande', // Here personnalise attr
'1' => 'Recherche', // Here personnalise attr
),
'expanded' => true,
))
I tried with the attr option, but it puts on all the radio buttons ... Or should that EVERY radio button is a different attr.
Thank you !
The expanded choice list in Symfony is actually a form with children of either type radio or type checkbox (depending on the multiple option being false or true respectively).
In my opinion, the best way to solve this, is create a custom type, and do your own templating of the type. This usually is the most clear to the fellow developer and is consistent (and much easier than you might think).
The type should look like this:
class MyType extends AbstractType()
{
public function getName()
{
return 'tooltip_choice';
}
public function getParent()
{
return 'choice';
}
}
Add this class to your service definitions and tag it as a form type (see docs on this).
Now, the form templating layer will search for a block named tooltip_choice_row (for the row of the element), tooltip_choice_widget for the widget of the element. If you inspect the default form templates, you can easily dissect how the choice list rendering works in the default implementation. You can either copy and paste it to your custom form theming and override.
The downside of this solution is that it costs a little bit more code, but you can much easier extend your types to include the tooltip itself, and pass that as an option to your form type as well.
Another solution would be to override the radio_widget and the checkbox_widget blocks in your form template, and check if the parent is a choice block. You can find out what exact variables you need by dumping the _context variable in your template, it shows exactly what values are available for you to check on.

Symfony2 - default values in Form Collection prototype

I'm trying to get the collection prototype to have a set of default values instead of blank values. Ideally I'd like to be able to define those default somewhere either in the model class or the form definition class, but I cannot find a way to do this anywhere.
As an example:
I've created an AbstractType for my form which contains a nested collection of Person rows (relevant code shown below):
public function buildForm(FormBuilder $builder, array $options)
{
...
$builder->add('people', 'collection', array(
'type' => new PersonType(),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
));
...
}
The PersonType class contains the following code:
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('name', 'text');
$builder->add('date_of_birth', 'date');
$builder->add('age', 'number');
// This would be great if I could do this but I can't:
//$builder->add('date_of_birth', 'date', array('empty_value' => new \DateTime(...))); // some default value defined here
}
The best I've been able to come up with so far is shown below in the view file (the code shown is used to render the collection prototype):
...
<tr>
<td> {{ form_widget(person.name) }} </td>
{# THIS DOES NOT WORK (I just get the default selected date) #}
<td> {{ form_widget(person.date_of_birth, {'value': person.date_of_birth.get('value')|default({'year':2010, 'month':10, 'day':15})} }} </td>
{# THIS WORKS (the field contains '0' instead of being empty) #}
<td> {{ form_widget(person.age, {'value': person.age.get('value')|default(0)} }} </td>
</tr>
...
It only seems to work with simple types like text and number. It doesn't work with the date type.
This anyway doesn't feel like the right approach. I should be able to define a default/empty value either in the underlying model (e.g. protected $age = 10; inside the model class), or else in the form definition (AbstractType) class (e.g. array('empty_value' => new DateTime()), but neither are currently possible.
So in summary, my question is:
How can I define default values for a model class that will be set automatically on the client when adding new items to a form 'collection' (instead of just getting blanks).
Does anyone know of a good way to do this?
In the constructor of the entity that the form is being used for, you simply set the date with a \DateTime object, like so:
class MyEntity {
private $myDate;
public function __construct() {
$this->myDate = new \DateTime('today');
}
}
You can also use \DateTime('now') or \DateTime('tomorrow'), as described in the discussion below
http://groups.google.com/group/symfony2/browse_thread/thread/18a5b20aca485dc4/e9947d0f06d6519d
Edit: Actually, this information is in the symfony2 documentation:
http://symfony.com/doc/2.0/book/forms.html#building-the-form
may be
$builder->setData(array('date_of_birth', new \DateTime(...)));
With Symfony >2.0 this is not possible.
Symfony 2.0 retrieved the values for the prototype from the underlying object so setting them in the constructor also changed the values in the prototype. However, this behavior was changed with Symfony 2.1 which removed this functionality, depraving us of the possibility of setting default values for the prototype:
I think setting a default value for the prototype is then indeed not possible right now. --webmozart, Symfony collaborator (https://github.com/symfony/symfony/issues/5087)
There is an open bug under active development which should add support for a data_prototype option. Using this option, it will be possible to supply data to prefill the prototype. However, this will probably be released no sooner than with Symfony 2.7.
As #Alexey Kosov mentioned in his comment and #Chris mentioned was going to be possible, you can now set the option 'prototype_data' => new YourEntity()
$builder
->add('your_field', CollectionType::class, [
'entry_type' => YourEntityType::class,
// ...
'prototype_data' => new YourEntity(),
])
;

Add CSS attributes to Symfony form labels?

I try to add some css attributes to labels in my custom sfForm but I can't achieve it.
In my custom class myForm extends sfForm, I create all textfields dynamically:
public function configure()
{
$widgetFixtures = array();
foreach ($fixtures as $fixture) {
$widgetFixtures[$fixture->getId()] = new sfWidgetFormInputText(
array('label' => $fixture->getTeamNameDom()),
// I would like to add something like: array('class' => $fixture->getCSS()),
array('value' => $fixture->getScore1(), 'readonly' => 'readonly')
);
}
$this->setWidgets($widgetFixtures);
}
I tried to format the rendering with setFormFormatterName but without success.
Note: I can't use renderLabel($value, $attributes = array()) in the template because I get the CSS class from the DB (as you may have seen, I have to use: $fixture->getCSS()).
Could someone shed my light?
Many thanks.
Here is how I solved it.
I took both suggestions from johnwards and richsage and put them together :
"This sort of stuff should be handled in the view/action."
"Access to the options/attributes passed to the Widget itself."
First, I add the CSS class to the input itself (even if I will not use it).
In my custom class myForm extends sfForm,
foreach ($fixtures as $fixture) {
$widgetFixtures[$fixture->getId()] = new sfWidgetFormInputText(
array('label' => $fixture->getTeamNameDom()),
array('value' => $fixture->getScore1(),
'readonly' => 'readonly',
'class' => $fixture->getCSS())
);
}
Then, in the template, instead of using echo $form;, I add the CSS class to the label as shown below:
foreach ($form as $widgetId => $widget) {
...
$labelClass = $widget->getWidget()->getAttribute('class');
echo '<td>'.$widget->renderLabel(null, array('class' => $labelClass)).'</td>';
...
}
Maybe it is not the best way to solve this issue but it works.
Thanks all for your feedback!
What you're trying to do seems possible but your greyed out syntax seems a little off. Try this:
$widgetFixtures[$fixture->getId()] = new sfWidgetFormInputText(
array('label' => $fixture->getTeamNameDom()),
array('class' => $fixture->getCSS()),
array('value' => $fixture->getScore1(), 'readonly' => 'readonly')
);
... or check "The sfWidgetForm Base Class" section here: http://www.symfony-project.org/forms/1_4/en/A-Widgets
The class attribute needs to be passed in as an array in the second parameter for all form widgets.
$this->setWidget('foo', new sfWidgetFormInputText(array(),array('class','fooclass'));
You could try overriding the widget class if you want to add CSS to labels - specifically the methods that render the label. You could then do something like
$foo = new myWidgetFormInputText(array("myCSSClass" => $fixture->getCSS()));
and then override your renderLabel() method or similar in your widget. Your widget will have access to the options you pass in - in the above example myCSSClass is the option key. You can then apply this class value to your widget's label.
It's much easier than that. Just apply CSS to the label for tag...
label[for=payment_cardNumber]
{
margin-top: 20px;
color: red;
}

Resources