Symfony, output form field more than once - forms

I have a tabbed form and I want to output a form field more than once. Say I have a tab called "individual" and another called "company". In the first tab I want to have the fields "name"and "address". In the second tab I want to have the fields "company" and "address", so I want to show the address form field twice (the user would select if they are an individual or a company by selecting the tab). But when I try to do this Symfony will not render the second address field because it has already been output.
Is there a way to override this behavior and have symfony output this field twice?

Unfortunately it seems that Symfony2 ( at least v2.2 and 2.3 ) only allow to render a form once because of Symfony2 form's internal handling ( token, id etc ).
The only way I have found is "tinkering", use twig set to capture form into a twig variable and then you will be able to use it more than once, ex :
{% set twig_form %}
<form method="POST" name="payment" action="{{ url('form_action') }}" {{ form_enctype(form) }}>
<label>My label</label>{{form_widget(form.input)}}
{{form_widget(form._token)}}
{% endset %}
{{twig_form}}
<button type="submit"></button>
</form>
<div>...</div>
{{twig_form}}
<button type="submit"></button>
</form>

Related

What is the recommended way to remove input name prefixes in symfony form

Say I have a form named myform with an input field named myinput. By default, symfony will generate HTML widget like:
<input name="myform[myinput]" ... >
This doesn't work with my current javascripts and I need to get the following instead:
<input name="myinput" ...>
I've searched quite a bit and found 2 ways to achieve this:
return null in getBlockPrefix() method in form type class.
create the form using FormFactoryInterface::createNamed() and pass null as name.
It seems like the first method is not recommended, since it would limit the ability to customize form rendering using prefixed blocks.
The 2nd method was recommended here and here.
However both methods will change the name of the form to null and as a result symfony will generate the form like:
<form name="" ...>
This is probably because form_div_layout.html.twig looks like:
<form name="{{ name }}" ...
Which doesn't validate as HTML5.
According to this page, "this is not a bug".
I know I can override the form_start block in the template and remove the name altogether, but it seems that the form wasn't designed to be used with null names in general (hence no check for name length in the template).
So my question is: What is the recommended and HTML5 compatible way to remove input name prefixes for symfony forms?
It was a bug in form rendering. I've submitted a pull request to symfony repo which was accepted.
Until the change is released, a temporary solution would be to add this code to your form theme:
{# fix HTML5 validation of forms with null names #}
{% block form_start %}
{% set name = name|default(block_prefixes.1) %}
{{ parent() }}
{% endblock %}
Regarding PHP, I think either option is OK.
Regarding Twig, I'd go for creating a custom form theme and, optionally, apply it to all forms on the site. I think that's the Symfony way of rendering a form according to your, specific, needs (nullable form name).

Symfony2 - Customizing buttons out of forms

I have problem with forms and page layouts. I render my page by:
{% block body -%}
{{ form(edit_form, {'style': 'horizontal'}) }}
<ul class="record_actions">
<li>
<a href="{{ path('organization') }}">
<button type="button" class="btn btn-default">Back to the list</button>
</a>
</li>
<li>
{{ form(delete_form) }}
</li>
</ul>
{% endblock %}
I have some style on ul record_actions. It looks like this: http://postimg.org/image/sby8jnojz/
My problem is with update button. I would like to put it into <ul> tags with 2 other buttons. Is there some possibility to put it outside of form? I like, how the form looks with {{ form(edit_form, {'style': 'horizontal'}) }}. So I wouldn't like to customize every part by {{form_widget}}. Or is there possibility to render all form and then render just this button?
Updated answer
Let's take a hypothetical controller method - where I have defined two forms edit_form and delete_form. Don't worry about these too much, they are just proof of concept. The important thing here is that I have two forms that I am sending to the template to be rendered:
// Foo\BarBundle\Controller\BazController
public function editAction()
{
// a placeholder 'edit' form
$editForm = $this->createFormBuilder()
->add('name', 'text')
->add('email', 'email')
->add('send', 'submit')
->getForm();
// a placeholder 'delete' form
$deleteForm = $this->createFormBuilder(['id' => 1])
->add('id', 'hidden')
->getForm();
// assign form views to template
return [
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
];
}
Next the template. We have two forms to render: edit_form and delete_form. There are a couple of issues we need to consider - rendering a form within a form is not allowed so we cannot render delete_form inside edit_form or vice versa.
However we can, as I explained below, with the HTML5 form attribute place form elements outside of a <form> context and still link them to that form (with the aforementioned IE* limitations). So let's do that, and suggest a workaround in due course.
The least invasive thing to do is to render the delete_form after the edit_form but place the edit_form delete button inside edit_form.
I don't know if you are using a CSS framewok to help you with layout - I am assuming Bootstrap 2.* here so you might have to update your markup - either way the idea should be clear enough:
<div class="row">
{{ form_start(edit_form, {'attr': {'id': 'edit-form'}}) }}
<div class="span4">
<ul class="record_actions">
<li>{{ form_widget(edit_form.send)}}</li>
<li><button id="delete-form-submit" form="delete-form">
Delete
</button>
</li>
</ul>
</div>
<div class="span4">
{{ form_rest(edit_form) }}
</div>
{{ form_end(edit_form) }}
</div>
{{ form(delete_form, {'attr': {'id': 'delete-form'}, 'method': 'GET'}) }}
The above HTML yields a layout similar to the following:
A few points of explanation:
I have created a two-column layout for the form. The .record_actions buttons are rendered on the left column - this is essential - but they are floated right using Bootstrap's .pull-right in this case.
Update button: the first thing I want to do is render the submit button in ul.record_actions where I want it: <li>{{ form_widget(edit_form.send)}}</li>
Delete button: I have not defined a submit button on the delete_form because I want to explicitly create it outside of the context of the delete_form, instead placing it where I have. Note that I defined a form attribute on this element called delete-form. This links this element to delete_form instead of edit_form: <li><button id="delete-form-submit" form="delete-form">Delete</button></li>
Remaining fields: in the second column I can dump all the remaining edit_form fields implicitly with {{ form_rest(edit_form) }}, as per #Kix's suggestion!
Delete form Finally, we render the delete_form outside the edit_form with {{ form(delete_form, {'attr': {'id': 'delete-form'}, 'method': 'GET'}) }}. A couple of things to note here - we are explicitly adding an id for the form with {'attr': {'id': 'delete-form'}. This is important as it is the attribute that the delete button refers to. In this case I also added 'method': 'GET' to test on my machine. You will probably want to leave this out (in which case it defaults to POST)
There you have it... This should help you define your preferred layout.
But, we still need to address IE. If you are using jQuery, you could add a click handler to the delete button, which we've assigned the id #delete-form-submit. Note that the following is a suggestion and is not tested:
$(function() {
if ($.browser.msie) { // #see: http://api.jquery.com/jquery.browser/
$('#delete-form-submit').on('click', function(e) {
e.preventDefault();
$('#delete-form').submit();
});
});
});
Now you need to worry about IE users with JS disabled... or not! ;) Hope this helps.
Original answer
I would argue that your issue is possible Symfony agnostic... Let me explain:
Do you create your form's submit button with the form builder? I assume so since you do not explicitly create one in the twig snippet you pasted above.
This is totally fine of course. I usually just define my Twig form templates like so, which I gather is the older way to do it (since the 2.4 docs don't appear to suggest the following):
<form class="form-horizontal" action="{{ path('foo_edit') }}" method="post" {{ form_enctype(edit_form) }}>
{{ form_widget(edit_form) }}
<input type="submit">
</form>
This way is totally acceptable in my view - you just don't define the submit button.
Of course this does not solve your problem, because you want to "break out" of the form. But you can actually do this with HTML5 with the form attribute, which allows you to link disparate tags to a specific tag. A generic example:
<form id="foo">
<label>Username</label>
<input type="text" name="username">
</form>
<ul>
<li><input type="submit" form="foo">
</ul>
Note that the submit button is outside form#foo but the form attribute still links to it.
Obviously its usefulness is restricted to the range of browsers you want to support, as it's a HTML5 feature.
EDIT
I checked - it has pretty wide browser support, except.... drumroll IE. Up to and including IE10 apparently. I would assume that this is a dealbreaker unfortunately.
kix's approach above could work well however, print out the individual form widgets you want explicitly and then use form_rest. I would add to this and say it might not exactly work with the HTML layout you have above - iirc you have to print out any fields explicitly before you call form_rest.
You can always render some widgets in places specific to your layout using
{{ form_widget(delete_form.yourWidgetName) }}
and then let Symfony complete the form with
{{ form_rest(delete_form) }}

Twig : Customize specific form field in form collection

I've got a form, with an embed form, and this embed form contains a form collection.
I can customize this collection doing :
{% block _form_refProspect_objects_widget %}
<div class="text_widget">
...
</div>
{% endblock %}
Having my form called form, the embed form called refProspect, and the collection called objects. That works, but what if I want to customize only one field ?
For example, having a field called name, none of thise works :
{% block _form_refProspect_objects_0_name_widget %}
{% block _form_refProspect_objects_name_widget %}
Is there any solution ?
Thanks !
EDIT :
At the same time I would like this customization to work on the form prototype, so I can use javascript to dynamically add some.
you can create your custom field type and add your logic to it.
a pretty good example would be How to Create a Custom Form Field Type
For example i use a custom CKEditor field type to make text fields as WYSIWYG editors. I used this bundle. You could check the source code to see how it works.

How to auto-fill form with URL params in Symfony2

I have a view in Symfony 2.3 that shows a full list of employee likes and dislikes. I am trying to create a simple form at the top of this view to filter the list by employee by name.
I have currently created the form within a twig template using regular HTML and some TWIG conditional statements to check if the employee_name GET param matches one of the employees. This solution works but isn't ideal.
<form action="{{ path('report') }}" method="GET">
<select id='employee_name' name='employee_name'>
<option value='John' {% if app.request.get("employee_name") == 'John' %} selected {% endif %}>John</option>
<option value='Aaron' {% if app.request.get("employee_name") == 'Aaron' %} selected {% endif %}>Aaron</option>
<option value='Sam' {% if app.request.get("employee_name") == 'Sam' %} selected {% endif %}>Sam</option>
</select>
<button type="submit">Submit</button>
</form>
My question is, is there a way to write the above form using the Form Builder and still have it auto-fill the fields when submitting? I'd like my filter form to be scalable and reusable. Perhaps it be would better to just use an Twig Include? Suggestions welcome.
Sure. Just create the form with the form builder and render it in Twig. Since you're using the GET method and if you're calling $form->handleRequest($request), the form will have the right values selected based on GET parameters.

Why is Symfony2 rendering a <input type="text"> instead of <input type="number"> for a form field of type number

In Symfony2 forms you have the following two from field types (among others):
number: http://symfony.com/doc/2.0/reference/forms/types/number.html
integer: http://symfony.com/doc/2.0/reference/forms/types/integer.html
My questions is why the integer type renders <input type="number"> and the number type renders <input type="text"> thus missing the HTML5 new input types attributes (min, max, step)?
Is there a decent and clean way to configure a number form type to render an <input type="number"> tag (form theming seems a bit dirty to me)?
Edit
Seems like changing the field template is the only option, even though it states a comment {# type="number" doesn't work with floats #}
Actually it could work for number too, if they set the attribute step="any". However, you can overwrite the block for the number-widget and change it to:
{% block number_widget %}
{% spaceless %}
{% set type = type|default('number') %}
{{ block('input') }}
{% endspaceless %}
{% endblock number_widget %}
Check this link, to see the default form layout template:
https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
Edit
This is only a wild guess, but maybe they are not setting number to not confuse the user.
When you're on an English system Chrome will accept a value like 1.9. When you type this into Chrome on a German system, the value will be corrected immediately to 19. On a German system you have to type 1,9. Chrome will change this to 1.9 on submit. Imagine the confusion for an unexperienced user who travels a lot. :)