How do you hide labels in a form class in symfony2? - php

I know that you can split a form out in twig and choose to not render the label for a particular field, but I can't help but think that you must be able to do this from the form class. The 'label' key in the options array lets you change this value to whatever you like, but passing either false or an empty string just returns the field name (see examples below where 'roles' is rendered as the label).
$builder
->add('roles', 'entity', array(
'class' => 'Acme\UserBundle\Entity\Role',
'label' => ''
));
$builder
->add('roles', 'entity', array(
'class' => 'Acme\UserBundle\Entity\Role',
'label' => false
));
Strangely, passing an empty space (which feels very dirty) seems to render a completely empty label, with no space even when viewing the source. Can anyone shed any light on the best approach, or even why the empty space seems to work?

Since Symfony 2.2 you can avoid the <label> rendering using the false value for the label attribute:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('Name', null, array('label' => false))
;
}
Source

Keep your 'View' specifications separate from your 'Model'
If you follow the accepted answer which says:
$builder
->add('Name', null, array('label' => false))
;
your form is not as re-usable. Especially if your form appears in more than one location (or might in the future).
If you do not want to render the form label it is best to do so in Twig (assuming your using Twig).
instead of rendering {{ form_row(form.name) }}, render each element separetly and exclude the form_label
ex.
{{ form_errors(form.name) }}
{# {{ form_label(form.name) }} <-- just dont include this #}
{{ form_widget(form.name) }}
If down the road you wanted the label in one instance of the form but the not the other, simply adding {{ form_label(form.name) }} would suffice; Where as changing array('label' => true) would turn the label on everywhere
If you are rendering your form with the one liner {{ form(form) }} then you should have a look at the symfony docs

Just add {'label':false} to your form_row()
{{ form_row(form.name, {'label':false}) }}

I don't understand very well your question but in form to show the name of label,personnaly I do like that :
$builder
->add('role', 'text')
in my twig :
<tr>
<td>{{ form_widget(form.role) }} </td>
<td>{{ form_label(form.role, "Name of Label") }}</td>
</tr>
<tr>
<td>{{ form_errors(form.role) }}</td>
</tr>

To hide my label, I had to render just the widget for the field, and not the label, e.g.
{{ form_widget(edit_form.event) }}
{{ form_rest(edit_form) }}
The problem with the ' ' label with a space in, is that it still renders the html input which is there and affects the page.

this should work (although its not a very clean solution)
$builder
->add('roles', 'entity', array(
'class' => 'Acme\UserBundle\Entity\Role',
'label' => ' '
));
(note the space between the ticks)

Related

How to merge Symfony form type attributes with attributes from form_row in twig

Suppose we have Symfony form type and a row adding field
$builder->add('name', 'text', ['attr' => ['class' => 'firstName', placeholder => 'first name']]);
I want this to be merged with attributes set in twig template:
{{ form_row(form.name, {'attr':{'class':'newClass'}}) }}
Currently it does replace. What is the best way to solve it?
You can use form variables to get form class attr, then concatene another class attr:
{{ form_row(form.name, {'attr':{'class':'newClass ' ~ form.name.vars.attr.class|default('')}}) }}
EDIT:
Corrected my previous answer after reading https://symfony.com/doc/current/reference/forms/twig_reference.html#reference-form-twig-variables

How to make sure that a Symfony form does not contain unnecessary elements?

I am a beginner in Symfony, so this question might be simple for those who are more experienced in this framework.
I am building forms and I have several possible types of items in forms. At this moment these are:
text
html
image
However, in the future there will be many more items. Previously, the project generated all items into their place (text, html and image) and hidden those which are not needed for the specific form item (maximum one is needed). However, I intend to avoid adding items I do not need. Since I do not know at the point buildForm of an arbitrary item is running whether it is text, html or image, so at this point all of them are added (I know this is counter-intuitive, but this is a code I try to refactor):
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('layoutTypeInput', TextType::class);
$builder->add('blockTypeOutput', EntityType::class, array(
'class' => 'MyPageBundle:BlockTypeOutput',
'choice_label' => 'titleHu',
'required' => false,
'placeholder' => 'Válassz blokk kimenetet!',
'empty_data' => null,
));
$builder->add('text', TextType::class, $this->getBlockTypeOptions('text'));
$builder->add('html', TextareaType::class, $this->getBlockTypeOptions('html'));
$builder->add('image', ImageSelectType::class, $this->getBlockTypeOptions('image'));
$builder->addEventListener(FormEvents::POST_SET_DATA, array($this, 'onPostSetData'));
$builder->addEventListener(FormEvents::PRE_SUBMIT, array($this, 'onPreSubmit'));
}
Now, I have a function which removes the unnecessary elements of a form:
private function removeUnnecessaryItems(\Symfony\Component\Form\FormInterface $form, $key)
{
$keys = ['text', 'html', 'image'];
foreach ($keys as $k) {
if ($key !== $k) $form->remove($k);
}
}
And inside onPostSetData I call it like this:
$this->removeUnnecessaryItems($form, $inputObject->getLayoutTypeIdText());
and finally, in the twig I determine what should be generated into the form:
{% for ioLayoutBlock in form.ioLayoutBlocks %}
<div class="row">
<div class="col-xs-12 col-md-3">
{{ form_errors(ioLayoutBlock.layoutTypeInput) }}
{{ioLayoutBlock.layoutTypeInput.vars.label}}
</div>
{{ form_widget(ioLayoutBlock.layoutTypeInput, {'attr' : {'class':'hidden'}}) }}
<div class="col-xs-12 col-sm-6 col-md-5">
{{ form_errors(ioLayoutBlock.blockTypeOutput) }}
{{ form_widget(ioLayoutBlock.blockTypeOutput, {'attr' : {'class':'blockTypeOutput'}}) }}
</div>
<div class="col-xs-12 col-sm-6 col-md-4">
{% if ioLayoutBlock.text is defined %}
{{ form_errors(ioLayoutBlock.text) }}
{{ form_widget(ioLayoutBlock.text, {'attr':{'class':'hidden uniqueInput ioLayoutBlock_text' }}) }}
{% elseif ioLayoutBlock.html is defined %}
{{ form_errors(ioLayoutBlock.html) }}
{% if layout.layoutType.name == 'userHTML' %}
<div class="input-group ioLayoutBlock_html hidden">
<a class="input-group-addon myAdminForm" target="_blank" data-my-href="page/{{ page.id }}/wysiwyg/{{ layout.id }}"><span class="glyphicon glyphicon-pencil"></span></a>
{{ form_widget(ioLayoutBlock.html, {'attr':{'class':'uniqueInput wysiwyg' }}) }}
</div>
{% else %}
{{ form_widget(ioLayoutBlock.html, {'attr':{'class':'hidden uniqueInput wysiwyg ioLayoutBlock_html' }}) }}
{% endif %}
{% elseif ioLayoutBlock.image is defined %}
{{ form_errors(ioLayoutBlock.image) }}
{{ form_widget(ioLayoutBlock.image, {'attr':{'class':'hidden uniqueInput ioLayoutBlock_image' }}) }}
{% endif %}
</div>
</div>
{% endfor %}
and if I load the page, everything is shown correctly, but unfortunately, when I try to submit the form, it gives the error of
This form should not contain extra fields.
as many times as many form items I have. If I comment out the call on removeUnnecessaryItems inside onPostSetData and subsequently remove the conditionals from the twig, like:
{% if ioLayoutBlock.text is defined %}
then everything works, but that's how it worked before the refactor. Ideally I would like to avoid adding so many unnecessary things at buildForm, but I do not know how can I load up any meaningful data there to determine the type of the item. Alternatively I would like to ensure that the items are successfully removed at the events where I do know their type, without the form errors on submit I described above. So, my question is: How can I avoid generating all kinds of unnecessary stuff into my form without being blocked by submit errors?
My approach would be to give a parameter to your form
$form = $this->createForm(DynamicType::class, $user, [
'layout_type_id' => $layoutTypeIdText,
]),
Then adds the fields depending of the param
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->layout_type = $options['LayoutTypeId'];
// [...]
if ($this->layout_type !== 'text' )
$builder->add('text', TextType::class, $this->getBlockTypeOptions('text'));
// [...]
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
// [...]
'layout_type_id' => null,
]);
}
With this approch, the good part is you don't to duplicate your logic in twig, the form get the fields it needs, so you can just render the form using
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
For your specific case you need to avoid adding the items at buildForm, to handle the default empty values at onPreSubmit (as the transformers will not be called if the items are not added at buildForm) and to add the effective items at onPreSubmit.
You were correct using form listeners, they are the way to go for your use case.
I see 2 sub scenarii:
the form data is coming from the Model, you fill it inside the controller
a new item is added to the form, client-side (via the prototype).
I'm getting from your example code that users can't add new items dynamically. If you want to do so, the code is just slightly different.
Remains the first scenario. The trick is not to remove, but to add forms inside the PRE_SET_DATA listener:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('layoutTypeInput', TextType::class);
$builder->add('blockTypeOutput', EntityType::class, array(
'class' => 'MyPageBundle:BlockTypeOutput',
'choice_label' => 'titleHu',
'required' => false,
'placeholder' => 'Válassz blokk kimenetet!',
'empty_data' => null,
));
$builder->addEventListener(FormEvents:: PRE_SET_DATA, array($this, 'onPreSetData'));
// ...
}
public function onPreSetData(FormEvent $event)
{
$data = $event->getData(); // This contains model data (ie., from controller)
$form = $event->getForm();
$type = 'image'; // Read type from your model
$formType = $this->getFormTypeForType($type);
$builder->add($type, formType, $this->getBlockTypeOptions($type));
}
private function getFormTypeForType($type)
{
switch ($type) {
case 'image':
return ImageSelectType::class;
// ...
default:
// Up to you, you can decide on setting a default type or enforcing that the type is correct
throw new \RuntimeException('Unsupported type');
}
}
With that code, you can keep the same Twig.
I'm not sure on what you are trying to do with layoutTypeInput and blockTypeOutput. Maybe we are answering just partially here, don't hesitate on posting the full use case.

Symfony 2.7 and rendering of choices in Twig

Trying to render a dropdown in Symfony 2.7.0 but I am having some issues when rendering the choices the view.
$form = $this->createFormBuilder(null)
->add('timespan', 'choice', array(
'choices' => array(90 => "3 months", 30 => "1 month")
))
->getForm();
...
return array(
'form' => $form->createView(),
);
...
Doing var_dump after this will display the values:
var_dump($form->get('timespan')->getConfig()->getOption('choices'));
But when rendering it in the view like this:
{{ form_widget(form.timespan, {'class': 'span2'}) }}
The select box becomes empty.
<select id="form_timespan" name="form[timespan]" required="required" class="span2"></select>
Any ideas why this might occur? Am I missing something?
The problem is obviously in Twig. You can debug this by editing the form theme, to see what values comes in and what is expected. The theme can be found at:
vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig
(Or you can configure Symfony to use your own.)
You're looking for {% block choice_widget %} specifically to render this.
In this case it looks like you've forgotten to use the attr key for your HTML class:
{{ form_widget(form.timespan, {'attr': {'class': 'span2'}}) }}
You forgot the attr key in your call :
{{ form_widget(form.timespan, {'attr': {'class': 'span2'}}) }}

Handling array paramter in html_anchor function of Twig in FuelPHP

I am converting the PHP based template to TWIG based template in FuelPHP.
echo Html::anchor('categories/create', 'Add', array('class' => 'btn'));
I found out with some help that the equivalent tag for TWIG is html_anchor which works fine. But as in this case, there is an third parameter passed as an array. How that can be converted for TWIG?
I tried the below found 2 lines and both failed with error so I assume its not the correct way.
{{ html_anchor('categories/create', 'Add', array('class' => 'btn')) }}
Twig arrays has [] format, so I tried this too.
{{ html_anchor('categories/create', 'Add', ['class' : 'btn']) }}
What's the correct way of handling this?
Here's how you define an associative array in twig:
{{ html_anchor('categories/create', 'Add', { 'class': 'btn' }) }}

Symfony2 - Twig render controller. Constraints form display

I need to render a controller in a template (principal.html.twig) with #Route and #Template annotations in order to create a form:
{{ render(controller('PprsBundle:Default:SupuestoConfig'), {'strategy': 'inline'}) }
Controller:
/**
* #Route("/configsup", name="configsup")
* #Template("PprsBundle:Default:SupuestoConfig.html.twig")
*/
public function SupuestoConfigAction()
{
...
->add('number', 'text', array(
'constraints' => new Length(array(
'min' => 1,
'max' => 2,
)),
....
}
routing.yml:
configsup:
resource: "#PprsBundle/Controller"
type: annotation
SupuestoConfig.html.twig:
<form id="configurador" action="{{ path('configsup') }}" method="POST">
<p class="titulo_configurador">Elija supuesto penal:</p>
{{ form_row(form.tipo) }}
{{ form_row(form.numero, { 'label' : ' ', 'attr' : { 'class' : 'rec3' }}) }}
{{ form_rest(form) }}
<input id= "btTipoSupuesto" type="submit" value="Cargar" class="inputbt"/>
</form>
I'm having an unexpected behaviour when the constraint is activated (when I introduce a 4 digits number in "number" field) because it only renders the view SupuestoConfig.html.twig showing the constraint error (route /configsup) instead of the whole page (principal.html.twig). How can i make it work?
You are sending the form to the SupuestoConfigAction which renders only the form. You should send your form to the action where you render the form originally (where you use {{ render(controller('PprsBundle:Default:SupuestoConfig'), {'strategy': 'inline'}) }. Handle the form submission in that action and display the result.
So it's not a strange behaviour. It behaves exactly as you tell your program to.

Categories