4

I Want to show all font awesome icon in select option with Symfony Form Builder.

I add the select field with :

$choices = $this->getFontAwesome();
$form->add( $key, ChoiceType::class, array('label' => 'Texte', 'choices' => $choices, 'attr' => array('class' => "fa" ) ) );

My Function getFontAwesome();

    public function getFontAwesome(){

    $webroot = $this->get('kernel')->getRootDir() . '/../web';
    $pattern = '/\.(fa-(?:\w+(?:-)?)+):before\s+{\s*content:\s*"\\\\(.+)";\s+}/';
    $subject =  file_get_contents( $webroot . '/assets/vendor/font-awesome/css/font-awesome.css');
    preg_match_all($pattern, $subject, $matches, PREG_SET_ORDER);
    foreach($matches as $match) {
        $icons[$match[1]] = '&#x' . $match[2] . ';' ;
    }

    return $icons ;

}

But in the select field, don't see the icon:

Field show the code and not the icon

enter image description here

How i can do ? I Try htmlspecialschars and others ( htmlentities, .. ) but don't work.

Noman
  • 1,260
  • 1
  • 16
  • 36
OcB974
  • 85
  • 1
  • 6

1 Answers1

4

If you aren't using any js plugins like Select2 or Bootstrap-select, then you have http://jsfiddle.net/NyL7d/ this possibility, but we need work a bit to reach it.

First, to say that using <i class="fa fa-heart"></i> as label isn't a choice, because the <option> element can't have any child elements, but only text. (see related issue)

For reusability let's build a form type named "IconChoiceType" as child of "ChoiceType":

namespace AppBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;

class IconChoiceType extends AbstractType
{
    /**
     * Cache for multiple icon fields or sub-requests.
     * 
     * @var array
     */
    private $choices;

    private $kernelRootDir;

    public function __construct($kernelRootDir)
    {
        $this->kernelRootDir = $kernelRootDir;
    }

    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        // Pass this flag is necessary to render the label as raw. 
        // See below the twig field template for more details.
        $view->vars['raw_label'] = true;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'attr' => [
                // It's the key of the solution and can be done in many ways.
                // Now, the rendered <select> element will have a new font.
                'style' => "font-family: 'FontAwesome';"
            ],
            'choices' => $this->getFontAwesomeIconChoices(),
        ]);
    }

    public function getParent()
    {
        return ChoiceType::class;
    }

    protected function getFontAwesomeIconChoices()
    {
        if (null !== $this->choices) {
            // don't to load again for optimal performance.
            // useful for multi-icon fields and sub-requests.
            return $this->choices;
        }

        // BTW we could configure the path to the "font-awesome.css".
        $fontAwesome = file_get_contents($this->kernelRootDir.'/../web/assets/vendor/font-awesome/css/font-awesome.css');

        // this regular expression only works with uncompressed version (not works with "font-awesome.min.css")
        $pattern = '/\.(fa-(?:\w+(?:-)?)+):before\s+{\s*content:\s*"\\\\(.+)";\s+}/';

        if (preg_match_all($pattern, $fontAwesome, $matches, PREG_SET_ORDER)) {
            foreach ($matches as list(, $class, $code)) {
                // this may vary depending on the version of Symfony, 
                // if the class name is displayed instead of the icon then swap the key/value
                $this->choices['&#x'.$code.';'] = $class;
            }
        }

        return $this->choices;

    }
}

and their respective service to register:

# app/config/service.yml
services:
    app.form.icon_choice_type:
        class: AppBundle\Form\Type\ChoiceIconType
        # Symfony has already a container parameter to the kernel root directory.
        arguments: ['%kernel.root_dir%']
        tags:
            - { name: form.type }

well, so far there is no result different from yours.

<select id="form_icon" name="form[icon]" style="font-family: 'FontAwesome';">
    <option value="fa-glass">&#xf000;</option>
    <option value="fa-music">&#xf001;</option>
    ...
</select>

enter image description here

Where is the problem now? The <select> font family is ready, but the icons they aren't showing, why?

By default, in Symfony the Twig environment escapes all values that are rendered using htmlspecialchars (more details), so we need overwrite this behavior for this form type only. For that, we create a fields.html.twig template in app/Resources/views/form directory and copy this code inside:

{# app/Resources/views/form/fields.html.twig #}

{# 
   here isn't need to create the expected `icon_choice_widget` like shown 
   the documentation, because this looks equal to `choice_widget` from 
   `ChoiceType`, only we need overwrite the block that renders the label. 
 #}

{%- block choice_widget_options -%}
    {% for group_label, choice in options %}
        {%- if choice is iterable -%}
            <optgroup label="{{ choice_translation_domain is same as(false) ? group_label : group_label|trans({}, choice_translation_domain) }}">
                {% set options = choice %}
                {{- block('choice_widget_options') -}}
            </optgroup>
        {%- else -%}

            {# this line has been overwritten, see {{- block('choice_option_label') -}} to end #}
            <option value="{{ choice.value }}"{% if choice.attr %} {% set attr = choice.attr %}{{ block('attributes') }}{% endif %}{% if choice is selectedchoice(value) %} selected="selected"{% endif %}>{{- block('choice_option_label') -}}</option>

        {%- endif -%}
    {% endfor %}
{%- endblock choice_widget_options -%}

{%- block choice_option_label -%}
    {# this block has been called from choice_widget_options block #}

    {%- if raw_label|default(false) -%}
        {# the label is rendered as raw when IconChoiceType is used #}
        {{ choice_translation_domain is same as(false) ? choice.label|raw : choice.label|trans({}, choice_translation_domain)|raw }}
    {%- else -%}
        {{ choice_translation_domain is same as(false) ? choice.label : choice.label|trans({}, choice_translation_domain) }}
    {%- endif -%}
{%- endblock -%}

Note that {{ choice.label|raw }} raw filter displays the raw text stored (it prevents from being escaped) into label, in this case the icon font content.

finally, you need to register the form theme like describe the documentation:

# app/config/config.yml

{# ... #}

twig:
    form_themes:
        - 'form/fields.html.twig'

Conclusion:

$form->add('icon', IconChoiceType::class);

enter image description here

Community
  • 1
  • 1
yceruto
  • 8,307
  • 5
  • 33
  • 61