Symfony2 - form theming based on field name - symfony

I want to create a choice_widget_collapsed theme, but only for one type of field, it can be checked by name, or by class name (it's entity field). Other choice field should be rendered by standard widget.
I've tried FIELDNAME_widget, CLASSNAME_widget, and I've searched in Google, but with no results.
EDIT
This is code of choice_widget_colapsed:
{% block choice_widget_collapsed -%}
{% if required and empty_value is none and not empty_value_in_choices and not multiple %}
{% set required = false %}
{% endif %}
<select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
{% if empty_value is not none -%}
<option value=""{% if required and value is empty %} selected="selected"{% endif %}>{{ empty_value|trans({}, translation_domain) }}</option>
{%- endif %}
{%- if preferred_choices|length > 0 -%}
{% set options = preferred_choices %}
{{- block('choice_widget_options') -}}
{% if choices|length > 0 and separator is not none -%}
<option disabled="disabled">{{ separator }}</option>
{%- endif %}
{%- endif -%}
{% set options = choices -%}
{{- block('choice_widget_options') -}}
</select>
{%- endblock choice_widget_collapsed %}
I want to modify HTML of this widget, only for fields named color.

Problem solved.
When you want to change html of widget, based by field name, you have to overwrite block:
{% block _FORMNAME_FIELDNAME_widget -%}
{# your html #}
{%- endblock %}

Related

how to check in twig if a form field has label

when a form field is generated via an iteration:
{% for field in form %}
{{ form_widget(field); }}
{% endform %}
then it will encapsulates a div block surround the field's input type, and a label (if it has a label).
I want to seperate this into label, and widget:
<div class="form-group">
{% if field.vars.label is not null %}
{{ form_label(field) }}
{% endif %}
{{ form_widget(field) }}
</div>
but the field.vars.label is always null. BTW when I generates without the condition, then it's shows labels correctly.
How do I get the label's value for the condition check?
Thank you in advance.
Change the label to false and the form_label() twig function will automatically ignore it.
Here's a code snippet from the form_div_layout.html.twig file (see line 2):
{%- block form_label -%}
{% if label is not same as(false) -%}
{% if not compound -%}
{% set label_attr = label_attr|merge({'for': id}) %}
{%- endif -%}
{% if required -%}
{% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' required')|trim}) %}
{%- endif -%}
{% if label is empty -%}
{%- if label_format is not empty -%}
{% set label = label_format|replace({
'%name%': name,
'%id%': id,
}) %}
{%- else -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>{{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}</label>
{%- endif -%}
{%- endblock form_label -%}
According to the doc of Form Variables Reference,
Try with:
{% if field.vars.attr.label is not null %}
instead of:
{% if field.vars.label is not null %}

Customising form labels in Symfony

I have a symfony page where I display a form, and I would like to add an
* in the labels of all fields that are required.
So I have my form.php.twig, that looks like this:
{% extends "::layout.php.twig" %}
{% block body %}
<div class="row">
{% use 'form_div_layout.html.twig' with form_label as base_form_label %}
{% block form_label %}
{{ block('base_form_label') }}
{% if required %}
<span class="required" title="This field is required">*</span>
{% endif %}
{% endblock %}
</div>
{% endblock %}
I followed the exact documentation of the symfony cookbook on how to customise labels, which is http://symfony.com/doc/current/cookbook/form/form_customization.html#cookbook-form-theming-methods.
But I keep getting this error
Variable "label" does not exist in form_div_layout.html.twig at line 206
I don't have any label variable in the code of my form.php.twig so I don't see why I get this error. And when I remove the
{{ block('base_form_label') }}
I get
Variable "required" does not exist in ATPlatformBundle:Session:create.php.twig
Can anyone help me on this? I don't see where is my mistake? I am not looking to customise it with css, but to add the * .
I have checked the form_div_layout.html.twig at line 206 and this is what there is
{%- block form_label -%}
{% if label is not sameas(false) -%}
{% if not compound -%}
{% set label_attr = label_attr|merge({'for': id}) %}
{%- endif %}
{% if required -%}
{% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' required')|trim}) %}
{%- endif %}
{% if label is empty -%}
{%- if label_format is not empty -%}
{% set label = label_format|replace({
'%name%': name,
'%id%': id,
}) %}
{%- else -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>{{ translation_domain is sameas(false) ? label : label|trans({}, translation_domain) }}</label>
{%- endif -%}
{%- endblock form_label -%}
Thank you in advance.
Did you try defining the label from inside your form builder?! The below field is a required one because unless you mention required => false, your field will be rendered as required.
Something like:
->add('name', 'text', array('label' => '* name'))
With Twig, you should have to test the existence of a variable before using it:
Variable "required" does not exist
{% if required is defined and ... }
You need to test this because you field isn't always required.
if you want further information, you have this page of the documentation:
http://twig.sensiolabs.org/doc/tests/defined.html
Ok, so at the end, I didn't manage to change labels within my form.php.twig (which is the template I use to display my form), but I used another technique.
I created a new file named fields.php.twig, which I put in MyBundle/Resources/views/Form.
At the beginning of my fields.php.twig, I then added
{% extends 'form_div_layout.html.twig' %}
and below it, I added
{% block form_label %}
{{ parent() }}
{% if required %}
<span> * </span>
{% endif %}
{% endblock form_label %}
Then in my form.php.twig, I added
{% form_theme form 'MyBundle:Form:fields.php.twig' %}
{{ form_start(form) }}
Myform here with {{ form_label(form.property) }}
{{ form_errors(form.property) }}
{{ form_widget(form.property) }}
{{ form_end(form) }}
It works perfectly, but I had to create the fields.php.twig.

Twig Form - Don't show label for checkbox

This might be simple but I have a label and checkbox overriden blocks in my form theme (among many others). I want all label to be handled by the form_label block except for checkboxs'.
I am currently wrapping the label rendering in an if statement which I don't want to do as it doesn't "feel clean":
{%- if form.vars.block_prefixes.1 is not defined or form.vars.block_prefixes.1 != 'checkbox' -%}
Here is are my overridden blocks, any chance I can disable the label in the checkbox_widget block?
{#
############# Checkbox #############
#}
{%- block checkbox_widget -%}
{% spaceless %}
<label for="{{ id }}"><input type="checkbox" {{ block('widget_attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />{{ label|raw }}</label>
{% endspaceless %}
{%- endblock checkbox_widget -%}
{#
############# Labels #############
#}
{%- block form_label -%}
{% if label is not sameas(false) -%}
{% set label_attr = label_attr|merge({'class': ('FormItem-label' ~ label_attr.class|default(''))|trim}) %}
{% if not compound -%}
{% set label_attr = label_attr|merge({'for': id}) %}
{%- endif %}
{% if required -%}
{% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' is-required')|trim}) %}
{%- endif %}
{% if label is empty -%}
{%- if label_format is not empty -%}
{% set label = label_format|replace({
'%name%': name,
'%id%': id,
}) %}
{%- else -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
{%- if form.vars.block_prefixes.1 is not defined or form.vars.block_prefixes.1 != 'checkbox' -%}
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>{{ label|trans({}, translation_domain) }}</label>
{%- endif -%}
{%- endif -%}
{%- endblock form_label -%}
{%- block button_label -%}{%- endblock -%}
You should use:
{{ form_label(form.terms_and_conditions) }}
Documentation
Added it to the twig template manually so as not to mess with the form theme.
{% block field_row %}
<div class="Grid-cell">
<div class="FormItem FormItem--checkbox is-required" required="required">
<label for="terms_and_conditions">
{{ form_widget(form.terms_and_conditions) }}
{% autoescape false %}{{ form.terms_and_conditions.vars.label }}{% endautoescape %}
</label>
{{ form_errors(form.terms_and_conditions) }}
</div>
</div>
{% endblock %}

Symfony2 form entity style each option differently (collapse)

I have an entity field (visitors) in a form. I would like to highlight a person according to its status.
->add('visitors', 'entity', array(
'class' => "T2m3Bundle:Person",
'multiple' => true,
'property' => 'entireName',
'required' => false,
)
I have a method getStatus, that returns 4 states. On every state I want to put different "style" in an option tag.
Here is the example:
<select>
<option value="1">Mary</option>
<option value="2" class="alert alert-danger">John</option>
<option value="3" class="alert alert-success">Peter</option>
<option value="4" class="alert alert-warning">Robert</option>
</select>
So I want to do something like this:
{% for visitor in form.visitors %}
{% set index = visitor.vars.value %}
{% set entity = form.visitors.vars.choices[index].data %}
{% if entity.status == 1 %}
{{ form_widget(visitor) }}
<span class="alert alert-warning">{{ entity.entireName }}</span>
{% endif %}
{% if entity.status == 2 %}
{{ form_widget(visitor) }}
<span class="alert alert-danger">{{ entity.entireName }}</span>
{% endif %}
....
{% endfor %}
This example works for 'expanded' => true but not for collapse type. I would be really thankful for an answer.
Thanks in advance!
EDIT:
Expanded use this template with form_widget(...):
{% block choice_widget_expanded -%}
<div {{ block('widget_container_attributes') }}>
{%- for child in form %}
{{- form_widget(child) -}}
{{- form_label(child) -}}
{% endfor -%}
</div>
{% endblock choice_widget_expanded %}
but I don't know how to render manually collapsed type, where there is no {{- form_widget(...) -}}
Try to override the twig choice widget : refer to this link to find the according block. It's something like this
{% block choice_widget_collapsed -%}
{% if required and placeholder is none and not placeholder_in_choices and not multiple -%}
{% set required = false %}
{%- endif -%}
<select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
{% if placeholder is not none -%}
<option value=""{% if required and value is empty %} selected="selected"{% endif %}>{{ placeholder|trans({}, translation_domain) }}</option>
{%- endif %}
{%- if preferred_choices|length > 0 -%}
{% set options = preferred_choices %}
{{- block('choice_widget_options') -}}
{% if choices|length > 0 and separator is not none -%}
<option disabled="disabled">{{ separator }}</option>
{%- endif %}
{%- endif -%}
{% set options = choices -%}
{{- block('choice_widget_options') -}}
</select>
{%- endblock choice_widget_collapsed %}
{% block choice_widget_options -%}
{% for group_label, choice in options %}
{%- if choice is iterable -%}
<optgroup label="{{ group_label|trans({}, translation_domain) }}">
{% set options = choice %}
{{- block('choice_widget_options') -}}
</optgroup>
{%- else -%}
{# HERE we define the class based on the status #}
{% if status is defined %}
{% if status == 1 %}
{% set status_class = 'alert alert-warning' %}
{% elseif status == 2 %}
{% set status_class = 'alert alert-danger' %}
{% elseif ... %}
....
{% endif %}
{% endif %}
<option value="{{ choice.value }}"{% if choice is selectedchoice(value) %} selected="selected"{% endif %} {% if status_class is defined %} class="{{ status_class }}" {% endif %} >{{ choice.label|trans({}, translation_domain) }}</option>
{%- endif -%}
{% endfor %}
{%- endblock choice_widget_options %}
Finally in your template, call the widget like this :
{% for visitor in form.visitors %}
{% set index = visitor.vars.value %}
{% set entity = form.visitors.vars.choices[index].data %}
{{ form_widget(visitor, {'status': entity.status}) }}
....
{% endfor %}
This is not tested, but should work. Let me know.

What is the difference between widget_attributes and widget_container_attributes?

Ok, bad question since semantically I think I can gather the difference by the block names themselves. My real question is how can I control which attributes appear a container and an element when widget_attributes and widget_containter_attributes are required on a given element.
Consider the following:
<div class="ui-select foo bar baz">
<select id="abc_hello_worldtype_name" name="abc_hello_worldtype[name]" class="thud grunt">
...
</select>
</div>
Main things I'm going after are having to set class names on both the div and the select. This is required for both style reasons as well as behavior-related requirements.
The main thing that is confusing me is that both the original widget_attributes and widget_container_attributes both use the attr variable passed in. Are these not intended to be used together?
I found myself doing something like the following today; just making my own blocks copied from the originals and adding to the conditionals. This all seems way too complicated. I know I'm doing it wrong.
{% block choice_widget_collapsed %}
{% spaceless %}
{% set attr = attr|merge({'class': (attr.class|default('') ~ ' ui-select')|trim}) %}
<div {{ block('ui_select_container_attributes') }}>
<select {{ block('ui_select_widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
{% if empty_value is not none %}
<option value=""{% if required %} disabled="disabled"{% if value is empty %} selected="selected"{% endif %}{% endif %}>{{ empty_value|trans({}, translation_domain) }}</option>
{% endif %}
{% if preferred_choices|length > 0 %}
{% set options = preferred_choices %}
{{ block('choice_widget_options') }}
{% if choices|length > 0 and separator is not none %}
<option disabled="disabled">{{ separator }}</option>
{% endif %}
{% endif %}
{% set options = choices %}
{{ block('choice_widget_options') }}
</select>
</div>
{% endspaceless %}
{% endblock choice_widget_collapsed %}
Notice the ui_* block references on the div and the select. Those blocks look like:
{% block ui_select_widget_attributes %}
{% spaceless %}
id="{{ id }}" name="{{ full_name }}"{% if read_only %} readonly="readonly"{% endif %}{% if disabled %} disabled="disabled"{% endif %}{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if pattern %} pattern="{{ pattern }}"{% endif %}
{% for attrname, attrvalue in attr %}{% if attrname in ['placeholder', 'title'] %}{{ attrname }}="{{ attrvalue|trans({}, translation_domain) }}" {% elseif attrname == 'class' %} class="foopa {{ attrvalue|replace({'ui-select':''}) }}" {% else %}{{ attrname }}="{{ attrvalue }}" {% endif %}
{% endfor %}
{% endspaceless %}
{% endblock ui_select_widget_attributes %}
{% block ui_select_container_attributes %}
{% spaceless %}
{% if id is not empty %}id="{{ id }}" {% endif %}
{% for attrname, attrvalue in attr %}{{ attrname }}="{{ attrvalue }}" {% endfor %}
{% endspaceless %}
{% endblock ui_select_container_attributes %}
When a form field is rendered as single form input (text input, select, checkbox...), widget_attributes is used. When it is rendered as a collection of inputs (form, multiple checkboxes, multiple inputs, ...), widget_container_attributes is used for the container surrounding the inputs (a div, mostly). So no, they are not intended to be used at the same time.
The difference between the two blocks is that widget_attributes renders form-specific attributes ("value", "name"...) while widget_container_attributes renders only generic HTML attributes.
If you want to add additional markup beyond the possibilities of the "attr" option, your best bet is to copy the corresponding block from the form theme (e.g. "choice_widget_collapsed"), paste it into your template, rename the block to match your element's ID with a leading underscore ("_") and a "widget" suffix (e.g. if your element's ID is "form_my_element", the block would be called "_form_my_element_widget") and modify the markup in your template.
{% block body %}
...
{{ form(form) }}
...
{% endblock %}
{% block _form_my_element_widget %}
... modified version of the "choice_widget_collapsed" markup ...
{% endblock %}

Resources