Sworoop Mahapatra
Sworoop Mahapatra

Reputation: 133

Calling form_widget multiple times with different attributes - Symfony & Twig

I was trying to call form widget in my form multiple times with different attributes. I am getting the error - Field "x" has already been rendered, save the result of previous render call to a variable and output that instead.

I have fixed in all the places that I could but there are some fields that need different attributers. For example :-

form_widget(foo.bar, {'value' : 'image'}) }}

and

form_widget(foo.bar, { 'attr': { 'class': 'hidden' }} )

Is there a possibility to call the form widget with different parameters on the same form?

Upvotes: 5

Views: 7135

Answers (1)

dbrumann
dbrumann

Reputation: 17166

Reusing the same widget multiple times, like you do can have weird side effects, because Symfony will also set a html id and depending on where you place the field you might end up with invalid html that can for example cause your JavaScript to break/do weird things or make submitting the forms flakey.

The safest way to ensure you have valid HTML (e.g. without duplicated id) is manually rendering each element and then only using the form elements to get the current content/errors. In order to do this, you can ignore the form_widget and form_row helpers and just write the HTML for the fields yourself and then only insert parts that you need, e.g.

<input name="{{ field_name(foo.bar) }}" type="file">

This exact example requires Symfony 5.2, because field_name() is new. See https://symfony.com/blog/new-in-symfony-5-2-form-field-helpers

The downside is, that things like {{ form_rest(form) }} will probably not work anymore, but at least you can still use any helper that only outputs part of the form that is not the form element, e.g. form_error, form_label should still work, but form_widget and form_row you should avoid. form_end renders all remaining fields again, which is why you probably should avoid that as well. The more specific helpers introduced in Symfony 5.2 are very handy for this approach as well. This way you have full control how the fields are rendered and can change the classes for each field that you use in multiple places.

If you use an older version of Symfony you can implement the helper methods yourself by taking a look at the TwigExtension that provides them and adjusting it for your app, e.g. provide your own AppTwigExtension with similar functions.

If you want to keep using form_widget/form_row then you should avoid rendering the same form (elements) multiple times in the same request. You can try using something like sub-requres/ESI, but it will probably be hacky.

There are way to render a form multiple times, allowing you to render it differently each time. For example you can create a FormCollection with the same type added twice. Then you have a single form that contains 2 forms (your form twice). You can also create multiple instances for the same form and then render them differently in your template based on which one you use (foo or bar), e.g. something like that (only rough outline, not sure if this exactly will work):

public function contact(Request $request): Response
{
    $form1 = $this->createForm(FooFormType::class, null, ['attr' => ['id' => 'form1']]);
    $form1->handleRequest($request);
    if ($form1->isSubmitted() && $form1->isValid() {
        // ...
    }

    $form2 = $this->createForm(FooFormType::class, null, ['attr' => ['id' => 'form1']]);
    $form2->handleRequest($request);
    if ($form2->isSubmitted() && $form2->isValid() {
        // ...
    }
    // Do something

    return $this->render('....html.twig', [
        'foo' => $form1->createView(),
        'bar' => $form2->createView(),
    ]);
}

As you can see that is a bit redundant, so in generally you want to avoid this approach, but you should find a few examples online of people doing something similar to that.

Upvotes: 12

Related Questions