1

In Symfony 3, I want the 'placeholder' of a select list menu to appear before any selection is made but to not appear (or not being select-able) among the list of available options for the user.

The function buildForm() of my MyEntityType, extending AbstractType looks like this:

public function buildForm(FormBuilderInterface $builder, array $options)
  {

    $list = array([array with choices values and descriptions]);
    $builder
    ->add('description',TextType::class, array('label'=>'trans.my_desc'))
  ->add('list',ChoiceType::class,array(
    'label'=>'trans.my_list',
    'multiple'=>false,
    'choices'=>$list,
    'placeholder'=>'trans.do_a_choice',
    //'placeholder_in_choices'=>false //this option is not available
)
->add('submit',SubmitType::class,array('label'=>'trans.validate'));

  }

When I look at the doc, I see that there is a boolean option placeholder_in_choices (http://symfony.com/doc/current/reference/forms/types/choice.html#field-variables) but this option cannot be set in the [array of options for list] which is under "->add('list',ChoiceType::class,array([array of options for list])" (it throws an error explaining that 'placeholder_in_choices' is not an available option).

Thru my search I found this already existing question close to mine: Symfony how to disable the default option

After reading thru it, I've tried to implement the finishView() function in MyEntityType class:

 public function finishView(FormView $view, FormInterface $form, array $options){

    var_dump($view->children['list']->vars);

   foreach ($view->children['list']->vars['choices'] as $sma) {
        if ($sma->value == "") {
            $sma->attr['disabled'] = 'disabled';
        }
   }
}

The problem is that, as it is shown in the var_dump(), $view->children['list']->vars['choices'] does not include the 'placeholder' value or label (trans.do_a_choice), it is not possible hence to attach a 'disabled' attribute to it.

Does anyone has a clue how to have 'trans.do_a_choice' to be displayed as placeholder in the select menu but to not appear (or not being select-able) in the options list?

Community
  • 1
  • 1
nyluje
  • 3,573
  • 7
  • 37
  • 67
  • Just ensure you set required to true on the field? Then when the form is set with the place holder set, it will throw an error because no value has been set... – LMS94 May 06 '16 at 09:48
  • I am not sure I understand. You want a placeholder to show, then as soon as the user click on the dropdown, to disappear? If that is the case, that sounds more like a frontend (JS) task. – Francesco Abeni May 06 '16 at 09:48
  • @LMS94 the "required" attribute on – nyluje May 06 '16 at 09:59
  • @Francesco Abeni, well I don't want it to necessarily disappear, I want the user to not be able to select it (so setting the – nyluje May 06 '16 at 09:59
  • If what you're trying to do is [this](http://stackoverflow.com/questions/5805059/how-do-i-make-a-placeholder-for-a-select-box) then Symfony providing the disabled attribute will be hacky. I'd use javascript for such a task and if the user doesnt have javascript, the form validation will prevent the default being posted. – LMS94 May 06 '16 at 10:36
  • Yes @LMS94 that's what I want (preferably with no JS lol). My understanding of "placeholder" is that it should not be displayed among the list of available options. – nyluje May 06 '16 at 10:43
  • No, place holder on choice (selects) will always add a default field. Why not just set a default value? – LMS94 May 06 '16 at 10:49
  • @LMS94, default value doesn't fit here, because I cannot let my user go for a default choice, he has to pick up something in the list that defines him. – nyluje May 06 '16 at 10:59

3 Answers3

0

I am currently using Symfony 4.4 and tried to find a way to solve the issue.

Solution 1: extending the default Symfony form template

This option does not seem to be available, so if you strictly want this result, one method is to override the default form_div_layout.html.twig form template by adding disabled="disabled" to the placeholder option tag.

To achieve this, you need to create a templates/form/form_div_layout.html.twig in your project (the update is on line 8).

{% extends 'form_div_layout.html.twig' %}
{%- block choice_widget_collapsed -%}
    {%- if required and placeholder is none and not placeholder_in_choices and not multiple and (attr.size is not defined or attr.size <= 1) -%}
        {% set required = false %}
    {%- endif -%}
    <select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
        {%- if placeholder is not none -%}
            <option value=""{% if required and value is empty %} selected="selected" disabled="disabled"{% endif %}>{{ placeholder != '' ? (translation_domain is same as(false) ? placeholder : placeholder|trans({}, translation_domain)) }}</option>
        {%- endif -%}
        {%- if preferred_choices|length > 0 -%}
            {% set options = preferred_choices %}
            {% set render_preferred_choices = true %}
            {{- block('choice_widget_options') -}}
            {%- if choices|length > 0 and separator is not none -%}
                <option disabled="disabled">{{ separator }}</option>
            {%- endif -%}
        {%- endif -%}
        {%- set options = choices -%}
        {%- set render_preferred_choices = false -%}
        {{- block('choice_widget_options') -}}
    </select>
{%- endblock choice_widget_collapsed -%}

Solution 2: using a pure CSS workaround

A pure css solution is a workaround that removes the placeholder option from the list when it's opened. The original code comes from How to create a placeholder for an HTML5 box

select:required:invalid {
  color: #666;
}

select:required option[value=""] {
  display: none;
}

option {
  color: #000;
}
<select name="drinks" required>
  <option value="" disabled selected>Choose a drink</option>
  <option value="coffee">Coffee</option>
  <option value="tea">Tea</option>
  <option value="milk">Milk</option>
</select>
Zoup
  • 101
  • 1
  • 5
-1

You must override the form theme for your form, or at least the choice_widget_collapsed block. If you use the default form_div_layout, then the related part looks something like this:

{%- if placeholder is not none -%}
    <option value=""{% if required and value is empty %} selected="selected"{% endif %}>{{ placeholder != '' ? (translation_domain is same as(false) ? placeholder : placeholder|trans({}, translation_domain)) }}</option>
{%- endif -%}

Here you can put the disabled attribute onto the option or any other attribute. Sadly choice_attr or any other option won't work for placeholder, so this is the only way I think.

More info about form themes: http://symfony.com/doc/current/form/form_customization.html

slaci
  • 111
  • 4
-1

If I understand correctly you want the placeholder to exist but not be accepted as a selected value?

To do that just add a choice constraint and you'll have the desired behavior. Form would not pass validation if user selects empty or placeholder value.

Something like this:

   use Symfony\Component\Validator\Constraints as Assert;

   public function buildForm(FormBuilderInterface $builder, array $options)
      {

        $list = array([array with choices values and descriptions]);
        $builder
        ->add('description',TextType::class, array('label'=>'trans.my_desc'))
      ->add('list',ChoiceType::class,array(
        'label'=>'trans.my_list',
        'multiple'=>false,
        'choices'=>$list,
        'placeholder'=>'trans.do_a_choice',
        'constraints' => array(
                            new Assert\Choice($valuesFromTheListOfChoices)
                        ),
    )
    ->add('submit',SubmitType::class,array('label'=>'trans.validate'));

      }

Very important is that the values passed in the assert/Choice match the values from the choices list. Meaning that if you select the placeholder value you must get a validation error.

As for the 'placeholder_in_choices' that option is resolved based on 'placeholder', 'choices' or 'preferred_choices'.

https://github.com/symfony/form/blob/3.1/Extension/Core/Type/ChoiceType.php#L204

It will be set to true if your choice list has an item with empty value.