Customize the rendering of a choice/entity field in Symfony2
Asked Answered
E

2

10

I would like a <select> element to be rendered with additional data on its <option>s. For the sake of example, I'd like to have a service-selector (non-multiple entity-field) that resets another inputs value upon selection change. I'm not interested in using JS data-structures, I need to have the rendered field to look as follows:

<select name="...">
    <option value="1" data-price="90">Service 1</option>
    <option value="2" data-price="40">Service 2</option>
</select>

I would take two different solutions and would be glad to see the answer to both of them.

  1. I'd render the field manually in Twig by starting to assemble the above HTML code by using the form variable I passed to the twig. I have two problems solving this. A) I can't find a safe way to tell what the filed should be named, i.e. how do I get the name attribute that Symfony expects by using the variable form.service (service is the name of the field in the FormType). [Please spare me tricks that concatenate some values based on observing how fields are currently named by Symfony; one should rely on interfaces and not on reverse engineering.] B) I don't know how to access the list of choices, i.e. the array assembled by the entity field's query_builder option. [Since I'm looking for a general solution, I'm not willing to duplicate these items over to a twig-parameter in the controller -- just to avoid such suggestions.]
  2. I'd override the rendering of the relevant field blocks, as suggested in the form styling chapter of the cookbook, but there are three problems with that. A) I cannot find out which blocks should be overridden (and so I don't find samples). B) I would pass parameter from the form builder to the block to let it know what extra data- attributes are to be rendered, but I don't know how to do this. And finally C) in those cases where I don't need to deviate from standard rendering (e.g. when the field is multiple) I don't know how to fallback to the default rendering.

So these are actually 5 questions (1A,1B,2A,2B,2C) but I thought them to be more useful to others answered together, since they all address what I think is an undocumented spot regarding choice field rendering.

Excommunication answered 28/3, 2013 at 14:42 Comment(5)
2A) That would be widget_choice_options.Blakeley
2B,partial) The FormType's $options [along with overriding setDefaultOptions] should suffice to get a flag into buildForm, that can add it to the choice field's parameters; the problem is that custom parameters are not permitted and result in 'The option "%s" does not exist'...Blakeley
I guess the following section of the doc can help on this subject: symfony.com/doc/current/cookbook/form/…Waylon
@cheesemacfly, thanks, but it won't fit :( "Another case that can appear is that you want to customize the form specific to the data that was submitted by the user." - It isn't related to data submitted by user. Nevertheless, I was coding and researching and I think I have already got quite nice solution using the type extension for choice type. When I'm done with this (hopefully) I'll post the answer :)Vina
For having done it before, the first 2 sentences of this question look really like something that can be solve with the doc I quoted. But I will wait to see your solution, maybe I don't get the whole problem!Waylon
S
19

1. Manually rendering. Better if is a individual form for field and not repeated somethere as it requires less time to work.

A) Get name of field you can with form.service.vars.full_name

B) List of choices - form.service.vars.choices. Its an array of ChoiceView, to get entity simply access public data property.

{% for choice in form.service.vars.choices %}
    {% set service_entity = choice.data %}
{% endfor %}

2. Via overriding templates. IF you like to brute-force pick up the name of the blocks which must be overriden.

A) You can only override widget, label and errors blocks as documentation said. You can specify block by widget name(documentation). Something like

{% block _form_service_widget %}
    {% if expanded %}
        {{ block('choice_widget_expanded') }}
    {% else %}
        {{ block('my_service_widget') }}
    {% endif %}
{% endblock %}

{% block my_service_widget %}
{% spaceless %}
    <select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
        {% if empty_value is not none %}
            <option value="">{{ empty_value|trans({}, translation_domain) }}</option>
        {% endif %}
        {% set options = choices %}
        {{ block('my_service_options') }}
    </select>
{% endspaceless %}
{% endblock my_service_widget %}

{% block my_service_options %}
{% spaceless %}
    {% for group_label, choice in options %}
        {# here you can access choice #}
        <option value="{{ choice.value }}"{% if choice is selectedchoice(value) %} selected="selected"{% endif %}>{{ choice.label|trans({}, translation_domain) }}</option>
    {% endfor %}
{% endspaceless %}
{% endblock my_service_options %}
Silverman answered 25/7, 2013 at 4:17 Comment(2)
How can you access to the selected value from a select type field with twig?Arris
By using this {% if choice.data in value %} selected="selected"{% endif %}Taeniafuge
S
3

You can use the choice_attr option in the form builder:

$builder->add('myField', ChoiceType:class, [
        ....
        'choice_attr' => function($val, $key, $index) {
            return ['data' => '...', 'class' => '', ... etc ];
        },
        ....
]);

This will apply attributes to each option, checkbox, radio in your choice (depending on your expanded and multiple option choices)

Stratocumulus answered 22/2, 2017 at 6:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.