Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony2 form: How to render the same element twice in the same view

Tags:

php

twig

symfony

I have a controller where I am creating a form witg two dropdown list inside.

When I am rendering my view, I would like to have the same form elements on the top and the bottom of the page. The problem is that the form elemetns (dropdownlists) are displayed only on the top of the page, even if I am asking twig to put them also on the bottom.

Here is what I would like to have:

enter image description here

The 1 and 2 are the dropdownlists. And I would like to duplicate this on the top and on the bottom of the page.

Any Idea on how can this be done?

The top content and the bottom content, where the two dropdownlists are inside are in a single sseparate twig file (searchPanel.html.twig) and this file is included in the page

{% include "MyBundle:Search:searchPanel.html.twig" %}

Here is the searchPanel.html.twig

<div class="searchPanel">

<form action="{{ path }}" method="POST" {{ form_enctype(form) }}>
    Papers per page
    {{ form_widget(form.papers_per_page, { 'class': 'ppp'}) }}

    / Sort by 
    {{ form_widget(form.sort_by, { 'class': 'sort'}) }}
    {{ form_rest(form) }}

    / Papers ({{ papers_number }} results)

 <input type="submit" class="updateSearchResults" value="Update"></input>
</form>
like image 342
Milos Cuculovic Avatar asked Feb 04 '13 08:02

Milos Cuculovic


2 Answers

A problem in your approach is that Symfony's Form-component will render the form elements with id's which would be duplicated if you rendered the same form twice on your page. You might also run in trouble with the csrf_token. The gist being that forms are not intended to be duplicated.

Here is what I would do. Create a twig-template containing your paginator form without using Symfony\Form, i.e. create all form elements statically and pass it the paginator-object (or array) to get the data instead of using form_widget(). Something like this:

<form action="{{ path(app.request.attributes.get('_route') }}" method="POST">
    <select name="paginator[per_page]">
        {% for per_page in paginator.papers_per_page %}
        <option value=""{{ per_page }}">{{ per_page }}</option>
        {% endfor %}
    </select>
</form>

The form action will automatically submit the data to your current route, so you can embed it in different actions and it will submit the data to the same action. On POST you can just create a paginator-object with the post-data and then add it as the form's data. After that you just use isValid() as usual.

In your controller you can get the data like this:

use Symfony\Component\HttpFoundation\Request;
// ...

public function PaperController()
{
    public function listAction(Request $request)
    {
        if ($request->getMethod() == 'POST') {
            $data = $request->request->get('paginator');
            $paginator = new Paginator($data);

            $form = new PaginatorFormType();
            $form->setData($paginator);

            if ($form->isValid()) {
                // ...
            }
        }
    }
}

You can easily embed the form in your view like this:

{{ include 'AcmeDemoBundle:Form:paginator.html.twig' with { 'paginator': paginator } }}

Basically you just use the Form-component in your controller for validation purposes. If you want to set some default values or add additional arguments you might want to create a macro from that template, but for your use case this should suffice. Maybe someone else has a better solution but this is how I went with a similar problem in one of my projects.

like image 93
dbrumann Avatar answered Oct 11 '22 18:10

dbrumann


another option is user the render twig helper. That way is is possible render the same form in the page as many time as you want. A difference is that using this helper, is also necessary to treat the form renderization as an independent controller Action namely: in every place in your twig template you want to render the form in the helper to invoke the form there's must be something like this:

{{ render(controller('BundleNameBundle:Controller:Action', {
    'param': paramId
})) }}

Thereafter is just a matter of creating the controller...

like image 34
didando8a Avatar answered Oct 11 '22 19:10

didando8a