wrap for fexibility

Overriding Symfony form fields – wrap radio and checkbox

By default Symfony form fields temaplte renders goups of checkboxes or radio buttons listed linearly. It’s just a queue of label-input tags. It’s sometimes irritating when Front End Developer demands to wrap each label-input in the separated div tag. Of course Symfony developers thought it through and it’s easy to use our own form fields templates.

Let’s code

In your bundle create a file Resources/views/Form/fields.html.twig

If you don’t have a Form directory, create it.

{%- block choice_widget_expanded -%}
    <div class="expanded-group" {{ block('widget_container_attributes') }}>
    {%- for child in form %}
		<div class="expanded-row">
			{{- form_widget(child) -}}
			{{- form_label(child, null, {translation_domain: choice_translation_domain}) -}}
		</div>
    {% endfor -%}
    </div>
{%- endblock choice_widget_expanded -%}

And just tell Twig to use your new template. In file app/config/config.yml find twig section and add the Symfony path to your file.

# Twig Configuration
twig:
    debug:            "%kernel.debug%"
    strict_variables: "%kernel.debug%"
    form_themes:
        - "form/layout.html.twig"
        - "form/fields.html.twig"
        - 'PiciosLibBundle:Form:fields.html.twig'

The line

 - 'PiciosLibBundle:Form:fields.html.twig'

should point to your bundle.

What we just did

We wrapped the loose label-input pairs in a div tag having a class expanded-row. It gives us more flexibility in styling those fields. Check also How to Customize Form Rendering.

Front End approach

We can achieve the same effect using Javascript. With JQuery it’s a piece of cake. Having such a HTML code:

<div id="user_permissions">
	<input type="checkbox" id="user_permissions_1" name="user[permissions][]" value="1" checked="checked" />
	<label for="user_permissions_1">User</label>
	<input type="checkbox" id="user_permissions_2" name="user[permissions][]" value="2" />
	<label for="user_permissions_2">Admin</label>
	<input type="checkbox" id="user_permissions_3" name="user[permissions][]" value="3" checked="checked" />
	<label for="user_permissions_3">Super Admin</label>
</div>

We can use the simple function like this:

$(function() {
	wrap('user_permissions');
});

function wrap(id) {
	$('#'+id+' label').each(function() {
		var $parent = $(this).closest('#user_permissions');
		var inputId = $(this).attr('for');
		var $input = $('#' + inputId);
		var $row = new $('<div class="expanded-row"></div>');
		$row.append($(this)).append($input);
		$parent.append($row);
	});
}