Symfony2 form entity style each option differently (collapse) - symfony

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.

Related

Passing attr.class into the form theme in symfony 5

My form theme:
{% block form_row %}
<div class="form_row {% if errors|length > 0 %} is_error {% endif %} {{ attr.class }}" {% if attr|length > 0 %} {% for key, value in attr %} {{key}}="{{value}}" {% endfor %} {% endif %}>
{% if block_prefixes.1 != "checkbox" %}
{{ form_label(form)|raw }}
{% endif %}
{% if icon != null and block_prefixes.1 != "checkbox" %}
<div class="input__icon-container {% if block_prefixes.2 == "password" %}input__password-container{% endif %}">
<div class="icon">
{{ source('#public_path' ~ asset('build/images/icons/' ~ icon|replace({"icon-":""}) ~ '.svg')) }}
</div>
{% if block_prefixes.2 == "password" %}
<span class="password-reveal">Show</span>
{% endif %}
{{ form_widget(form, { 'attr': {'class': 'form-control'} }) }}
</div>
{{ form_errors(form) }}
{% else %}
{% if block_prefixes.2 == "password" %}
<div class="input__password-container">
<span class="password-reveal">Show</span>
{% endif %}
{{ form_widget(form, { 'attr': {'class': 'form-control'} }) }}
{% if block_prefixes.2 == "password" %}
</div>
{% endif %}
{{ form_errors(form) }}
{% endif %}
</div>
{% endblock %}
{% block radio_widget %}
<div class="label">{{ label|raw }} {% if help != null %} <span class="help">{{help}}</span> {% endif %}</div>
{% for choice in choices %}
<div class='input__choice-container'>
<label class="is_regular"> <input type="radio" name="{{ full_name}}" {% if required == "true" %} required="required" {% endif %} value="{{ choice.value }}"><span class="radio"></span> <span>{{ choice.label }}</span> </label>
</div>
{% endfor %}
{% endblock %}
{% block checkbox_widget %}
<div class="custom-checkbox custom-control">
<input type="checkbox" class="custom-control-input" id="{{id}}" name="{{ full_name}}">
<label class="custom-control-label" for="{{id}}">{{label|raw}}</label>
</div>
{% endblock %}
The error I got:
Key "class" does not exist as the array is empty.
But that's not accurate because this is my form output for this form row:
{{ form_row(registrationForm.displayname, {
attr : { "class" : "mb-1", "data-test" : "tests"}
})}}
So the question is here, how do I output the class content in the appropriate manner? Clearly I'm doing it wrong.
It is because your template doesnt print class literally
Understand the structure of forms here Form Customization
You will need to inject attribute print logic just like one of the built-in templates found( bootstrap 4 example) in Form Themes
{% block form_row -%}
{%- set widget_attr = {} -%}
{%- if help is not empty -%}
{%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%}
{%- endif -%}
<div{% with {attr: row_attr|merge({class: (row_attr.class|default('') ~ ' form-group' ~ ((not compound or force_error|default(false)) and not valid ? ' has-error'))|trim})} %}
{{ block('attributes') }}
{% endwith %}>
{{- form_label(form) }} {# -#}
{{ form_widget(form, widget_attr) }} {# -#}
{{- form_help(form) -}}
{{ form_errors(form) }} {# -#}
</div> {# -#}
{%- endblock form_row %}
If you don't want to do it over, extend built in form widgets which has the printing logic.
Or simply work out on your block to print these passed attributes.

KnpMenuBundle add icon/glyphicon

I have tried to set an icon next to the link of the menu generated by knpMenuBundle but no way to get it.
I found some solutions like ->setAttribute or ->setExtra, but I can't make it work.
Do I have to add specific lines to the base template of knp ?
Do something special in the twig render ?
Here is what I have tried so far:
//Builder.php
$public function mainMenu(FactoryInterface $factory, array $options)
{
$menu = $factory->createItem('root');
$menu->setChildrenAttribute('class', 'nav navbar-nav ');
$menu->addChild('Mes Informations',['route' => 'espace_client_mesInformations'])
->setAttribute('icon','fa fa-home');
$menu->addChild('Mes Factures', array('route' => 'espace_client_mesFactures'));
$menu->addChild('Mon Extrait de Compte', array('route' => 'espace_client_mesReglements'));
$menu->addChild('Mes Services', array('route' => 'espace_client_mesServices'));
$menu->addChild('Mes Consommations', array('route' => 'espace_client_mesConsommations'));
$menu['Mes Consommations']->addChild('Télephone', array('route' => 'espace_client_mesConsoTelephone'));
$menu['Mes Consommations']->addChild('Internet', array('route' => 'espace_client_mesConsoInternet'));
$menu->addChild('Mes Liens Directs', array('route' => 'espace_client_mesLiens'));
return $menu;
}
//menu.html.twig
<div class="panel panel-default">
<div class="panel-heading">
<center><h4> Menu </h4></center>
</div>
<div class="panel-body">
{{ knp_menu_render('EspaceClientBundle:Builder:mainMenu',{'currentAsLink':false, 'template': 'EspaceClientBundle:Menu:knp_menu.html.twig'}) }}
</div>
If anyone have an answer ?
Thanks.
Edit:
I'm a little bit confused by the different files of the bundle, I must have a problem in my php code so there is the content of them:
//knp_menu.html.twig:
{% extends 'knp_menu_base.html.twig' %}
{% macro attributes(attributes) %}
{% for name, value in attributes %}
{%- if value is not none and value is not same as(false) -%}
{{- ' %s="%s"'|format(name, value is same as(true) ? name|e : value|e)|raw -}}
{%- endif -%}
{%- endfor -%}
{% endmacro %}
{% block compressed_root %}
{% spaceless %}
{{ block('root') }}
{% endspaceless %}
{% endblock %}
{% block root %}
{% set listAttributes = item.childrenAttributes %}
{{ block('list') -}}
{% endblock %}
{% block list %}
{% if item.hasChildren and options.depth is not same as(0) and item.displayChildren %}
{% import _self as knp_menu %}
<ul{{ knp_menu.attributes(listAttributes) }}>
{{ block('children') }}
</ul>
{% endif %}
{% endblock %}
{% block children %}
{# save current variables #}
{% set currentOptions = options %}
{% set currentItem = item %}
{# update the depth for children #}
{% if options.depth is not none %}
{% set options = options|merge({'depth': currentOptions.depth - 1}) %}
{% endif %}
{# update the matchingDepth for children #}
{% if options.matchingDepth is not none and options.matchingDepth > 0 %}
{% set options = options|merge({'matchingDepth': currentOptions.matchingDepth - 1}) %}
{% endif %}
{% for item in currentItem.children %}
{{ block('item') }}
{% endfor %}
{# restore current variables #}
{% set item = currentItem %}
{% set options = currentOptions %}
{% endblock %}
{% block item %}
{% if item.displayed %}
{# building the class of the item #}
{%- set classes = item.attribute('class') is not empty ? [item.attribute('class')] : [] %}
{%- if matcher.isCurrent(item) %}
{%- set classes = classes|merge([options.currentClass]) %}
{%- elseif matcher.isAncestor(item, options.matchingDepth) %}
{%- set classes = classes|merge([options.ancestorClass]) %}
{%- endif %}
{%- if item.actsLikeFirst %}
{%- set classes = classes|merge([options.firstClass]) %}
{%- endif %}
{%- if item.actsLikeLast %}
{%- set classes = classes|merge([options.lastClass]) %}
{%- endif %}
{# Mark item as "leaf" (no children) or as "branch" (has children that are displayed) #}
{% if item.hasChildren and options.depth is not same as(0) %}
{% if options.branch_class is not empty and item.displayChildren %}
{%- set classes = classes|merge([options.branch_class]) %}
{% endif %}
{% elseif options.leaf_class is not empty %}
{%- set classes = classes|merge([options.leaf_class]) %}
{%- endif %}
{%- set attributes = item.attributes %}
{%- if classes is not empty %}
{%- set attributes = attributes|merge({'class': classes|join(' ')}) %}
{%- endif %}
{# displaying the item #}
{% import _self as knp_menu %}
<li{{ knp_menu.attributes(attributes) }}>
{%- if item.uri is not empty and (not matcher.isCurrent(item) or options.currentAsLink) %}
{{ block('linkElement') }}
{%- else %}
{{ block('spanElement') }}
{%- endif %}
{# render the list of children#}
{%- set childrenClasses = item.childrenAttribute('class') is not empty ? [item.childrenAttribute('class')] : [] %}
{%- set childrenClasses = childrenClasses|merge(['menu_level_' ~ item.level]) %}
{%- set listAttributes = item.childrenAttributes|merge({'class': childrenClasses|join(' ') }) %}
{{ block('list') }}
</li>
{% endif %}
{% endblock %}
{% block linkElement %}
{% import "knp_menu.html.twig" as macros %}
<a href="{{ item.uri }}"{{ macros.attributes(item.linkAttributes) }}>
<span class="icon">{{ item.getExtra('icon') }}</span>
<span class="entry">{{ block('label') }}</span>
</a>
{% endblock %}
{% block spanElement %}{% import _self as knp_menu %}<span{{ knp_menu.attributes(item.labelAttributes) }}>{{ block('label') }}</span>{% endblock %}
You should edit your menu template EspaceClientBundle:Menu:knp_menu.html.twig like:
{% block label %}
{% if options.allow_safe_labels and item.getExtra('safe_label', false) %}
{{ item.label|raw|trans }}
{% else %}
{{ item.label }}
{% endif %}
{% if item.extras.icon is defined %}
<i class="{{ item.extras.icon }}"></i>
{% endif %}
{% endblock %}
I recommend you, to delete the whitespaces between twig tags.
Then, in menu builder:
$menu->addChild('Mes Informations', [
'route' => 'espace_client_mesInformations',
'extras' => ['icon' => 'fa fa-home']
]);

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 theming based on field name

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 %}

How to show label for overrided checkbox in Symfony Twig

I've overrided the Twig template for my forms in my symfony application so that I could have more control over the labels for checkboxes.
However, I am having problems with the checkbox label.
Here is the code I overrided in my custom template file:
{# Labels #}
{% block form_label %}
{% spaceless %}
{% 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 %}
{% set label = name|humanize %}
{% endif %}
{% endif %}
{% if 'checkbox' not in block_prefixes %}
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>{{ label|trans({}, translation_domain) }}</label>
{% endif %}
{% endspaceless %}
{% endblock form_label %}
{# Checkboxes #}
{% block button_label %}{% endblock %}
{% 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 }}</label>
{% endspaceless %}
{% endblock checkbox_widget %}
It works fine but I can't get the label text node for checkboxes working.
When I have a checkbox it generates something like:
<label><input type="checkbox"/></label>
Where it should be:
<label><input type="checkbox"/>Label Here</label>
Any clue on how to make the label string apear after the checkbox?
Edit:
I came to a solution, that worked pretty fine, but I am not sure if it the best.
{% block form_row %}
{% spaceless %}
<div>
{{ form_errors(form) }}
{% if 'checkbox' in block_prefixes %}
{{ form_widget(form) }}
{{ form_label(form) }}
{% elseif 'radio' in block_prefixes %}
{{ form_widget(form) }}
{{ form_label(form) }}
{% else %}
{{ form_label(form) }}
{{ form_widget(form) }}
{% endif %}
</div>
{% endspaceless %}
{% endblock form_row %}
You can remove the label in {% block choice_widget %} to prevent it from appearing in the default spot.
{% block choice_widget %}
{% spaceless %}
{% if expanded %}
<div {{ block('widget_container_attributes') }}>
{% for child in form %}
{{ form_widget(child) }}
{{ form_label(child) }} {# HERE #}
{% endfor %}
</div>
{% else %}
//....
{% endspaceless %}
{% endblock choice_widget %}
If you do so, you would have to override the {% block radio_widget %} too. Otherwise it won't have a label.
<label for="{{ id }}"><input type="radio" {{ block('widget_attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />{{ label|trans }}</label>
You can then remove the {% if 'checkbox' not in block_prefixes %} line you put in {% block form_label %}.
It worked for me.
Symfony2 - How to put label and input for checkboxes/radios in a same line?
Edit :
it seems that they split the {% block choice_widget %} in 2.3.3. You have to edit the choice_expanded block now, and remove the label line as above.
{% block choice_widget_expanded %}
{% spaceless %}
<div {{ block('widget_container_attributes') }}>
{% for child in form %}
{{ form_widget(child) }}
{{ form_label(child) }} {# There #}
{% endfor %}
</div>
{% endspaceless %}
{% endblock choice_widget_expanded %}

Resources