4

I'd like to create a custom template in twig to render a form field.

Example:

{{ form_row(form.field) }}

This can be overriden by form theming

{% block form_row %}
... custom code
{% endblock form_row %}

What I would like to do is this:

{% block custom_row %}
... custom code
{% endblock custom_row %}

and use it like this:

{{ custom_row(form.field }}

however, this throws an exception that method custom_row is not found.

My understanding is that this can be done with Twig extension, but I don't know how to register a block to be a function.

Update

what I actually want:

I use twitter bootstrap and a bundle which overrides all the form themes. And it renders a div around a radio, so it can't be inlined. So I wanted to do something like this:

copy their template and get rid of the div:

{% block inline_radio_row %}
    {% spaceless %}
        {% set col_size = col_size|default(bootstrap_get_col_size()) %}

        {% if attr.label_col is defined and attr.label_col is not empty %}
            {% set label_col = attr.label_col %}
        {% endif %}
        {% if attr.widget_col is defined and attr.widget_col is not empty %}
            {% set widget_col = attr.widget_col %}
        {% endif %}
        {% if attr.col_size is defined and attr.col_size is not empty %}
            {% set col_size = attr.col_size %}
        {% endif %}

        {% 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 %}
            {% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' radio-inline')|trim}) %}
            <label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>
                {{ block('radio_widget') }}
                {{ label|trans({}, translation_domain) }}
            </label>
        {% else %}
            {{ block('radio_widget') }}
        {% endif %}
        {{ form_errors(form) }}
    {% endspaceless %}
{% endblock inline_radio_row %}

and then

{{ inline_radio_row(form.field) }}

I ended up just overriding the whole theme, and added ifs around the div in question, a the class (radio-inline). But I'm still wondering if there's a way to make this work. Seems like it makes you work so hard for something so simple.

Update 2

I found the functionality:

class FormExtension extends \Twig_Extension
{
    public function getFunctions()
    {
        return array(
            'inline_radio_row'  => new \Twig_Function_Node(
                'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode',
                array('is_safe' => array('html'))
            ),
        );
    }
}

This does exactly what I want, but it says it's deprecated. Anyone knows an updated version of how to use this?

Update 3

Similar functionality can be also achieved with http://twig.sensiolabs.org/doc/tags/include.html

Necros
  • 3,004
  • 23
  • 29
  • Do you want this for one or several fields? – A.L Dec 14 '13 at 17:56
  • one, though i dont see how it makes a difference – Necros Dec 14 '13 at 18:00
  • It's easier to personalize one field than defining a custom template. – A.L Dec 14 '13 at 18:02
  • What is your goal? What is the expected result? – A.L Dec 14 '13 at 18:25
  • 1
    My goal is to create a function that can be called from twig, just like form_row, form_widget, radio_row.. etc, that will render my template. I understand there might be workarouds... right now I have overriden a default template (in my case radio_row) and added a few if to accomplish what I want. But I'm curious if there's a way to define custom templates. It's a useful thing to have something like partial views in ASP.NET MVC. – Necros Dec 14 '13 at 18:57
  • If [macros](http://twig.sensiolabs.org/doc/tags/macro.html) aren't suitable, can you explain why? :) – Martin Lie Dec 15 '13 at 04:28
  • didnt look into macros before. they might be useful indeed. but they don't get passed the context and option the same way as the form functions. Let me update the question with what im actually doing. – Necros Dec 15 '13 at 11:06
  • Did you try to add a CSS class to the div row in order to override the bootstrap CSS rules? It should be easier than rewriting all the form field. – A.L Dec 15 '13 at 11:38
  • It probably could be done, but why would i do that when there's a predefined way to do it with boostraps default css. Anyway the point is I want to know if what I'm talking about in my question is possible. – Necros Dec 15 '13 at 12:18
  • Ok, now that I know what you're trying to achieve, it sounds like what you need is [application-wide form rendering customization](http://symfony.com/doc/current/cookbook/form/form_customization.html#making-application-wide-customizations). :) – Martin Lie Dec 16 '13 at 00:23
  • Links to official tutorials aren't gonna help. I've been through all of them. If you know how to do exactly what is described in the question please write an answer explaining how. – Necros Dec 16 '13 at 12:04
  • question updated with latest findings. – Necros Dec 16 '13 at 13:44

2 Answers2

1

You can use the twig functions for each part of a form row:

For example:

<div class="form_row">
    {{ form_label(form.field) }} {# the name of the field #}
    {{ form_errors(form.field) }} {# the field #}
    {{ form_widget(form.field) }} {# the errors associated to the field #}
</div>
sdespont
  • 13,915
  • 9
  • 56
  • 97
A.L
  • 10,259
  • 10
  • 67
  • 98
  • yes, I know i can do that. But that wasn't the question. The example is a simple one, but I need the solution for a more complex case. – Necros Dec 14 '13 at 18:22
0

You can use form theming.

Step by step:

1. Form Type Class

Check the name in your class

public function getName() {
  return 'hrQuestionResponse';
}

2. Include a custom theme in your template

{% form_theme form 'InterlatedCamsBundle:Form:fields.html.twig' %}

3. Find the block

Can be quite difficult. For the bootstrap bundle, as you seem to have found it is in ./vendor/braincrafted/bootstrap-bundle/Braincrafted/Bundle/BootstrapBundle/Resources/views/Form/bootstrap.html.twig and you have found the block radio_row. I have been finding the block by putting output in the source template and overriding more blocks than I need. In 2.7 there is a theme 'rendering call graph'.

4. Override the block

Copy the block from the master template and call it replace the standard term with the name of in the FormType found in step 1.

Don't forget to also change the endblock name.

e.g.

{% block hrQuestionResponse_widget %}
hrQuestionResponse_row
{% spaceless %}
    {% set class = '' %}
...
 {% endspaceless %}
{% endblock hrQuestionResponse_widget %}

In your case because you can only call form_widget() you will need to override _widget. You could extract only the content that you need, or you can override the block chain to radio_row.

Community
  • 1
  • 1
Interlated
  • 5,108
  • 6
  • 48
  • 79