2015-08-17 4 views
5

Перед использованием Symfony 2.7 значение attr для поля choice применяется только к самому полю, то есть к элементу <select>, который был визуализирован. Я использовал это, чтобы применить классы к этому элементу для его стилизации.Как я могу избежать применения attr ко всем опциям моего поля выбора?

В Symfony 2.7 это поведение было изменено. Теперь все <option> детей элемента <select> также имеют те же атрибуты (commit of the change) и поэтому классы.


Для некоторых разъяснений, пусть это будет код:

<?php echo $view['form']->widget($form['myField'], ['attr' => ['class' => "text ui-widget-content ui-corner-all"]]); ?> 

Тогда это выход Symfony < = 2,6:

<select class="text ui-widget-content ui-corner-all" name="myField"> 
    <option value="1">Option 1</option> 
    <option value="2">Option 2</option> 
</select> 

И это выход Symfony> = 2,7:

<select class="text ui-widget-content ui-corner-all" name="myField"> 
    <option value="1" class="text ui-widget-content ui-corner-all">Option 1</option> 
    <option value="2" class="text ui-widget-content ui-corner-all">Option 2</option> 
</select> 

Классы, которые я применяю, не подходят для элементов <option>, поскольку они определяют границы и т.п. для фактического поля. Обратите внимание, что это классы, определенные jQuery UI, поэтому я не могу легко изменить их определение.

Что является самым простым способом избежать применения этих классов ко всем <option> элементам поля choice, все еще применяя его к элементу <select>?

+0

Возможно, это ошибка. Потому что я не мог найти что-либо в UPGRADE-2.7 для этого, и эта функциональность - это то, что должен делать параметр '' choice_attr''. – user2268997

+0

@ user2268997 Да, я ничего не мог найти, но, глядя на код, который я связал, он кажется очень преднамеренным для меня. – Chris

+0

Я мало что знаю о шаблонах php, но похоже, что «attr» разрешен из '$ choice', который является экземпляром' ChoiceView', который создается 'DefaultChoiceListFactory', и передается опция' choice_attr' на этот завод в определении 'ChoiceType'. (если вы не предоставляете собственный« ChoiceList ») – user2268997

ответ

4

Благодаря комментарии о choice_attr от @ user2268997 я нашел соответствующий пост New in Symfony 2.7: Choice form type refactorization блог, который детализирует использование (как сейчас незарегистрированные) choice_attr вариант.

Кажется, что Symfony объединяет атрибуты в choice_attr с номерами в attr при рендеринге поля. Это означает, что нам нужно перезаписать атрибут class в choice_attr.

Я попытался сделать это в коде рядом с тем, где я определяю attr, но не повезло. Кажется, вам нужно сделать это в определении типа формы. Вот отрывок из моей формы после добавления опции choice_attr:

namespace MyBundle\Form; 

public function buildForm(FormBuilderInterface $builder, array $options) { 
    $builder 
     ->add('roles', 
      'entity', 
      [ 
       'class' => 'MyBundle:Role', 
       'choice_label' => 'name', 
       'multiple' => true, 
       'choice_attr' => function() { return ["class" => ""]; } 
      ]); 
} 

В результате, как я надеялся. Я, вероятно, также реорганизую это на свой собственный тип формы, поэтому мне не нужно повторять его по всему набору.


Теперь я решил создать пользовательский тип choice с желаемым поведением, описанным выше, и использовать это один в течение моего приложения.

Вот мой тип выбора:

use Symfony\Component\Form\Extension\Core\Type\ChoiceType; 
use Symfony\Component\OptionsResolver\OptionsResolver; 

class ChoiceNoOptAttrType extends ChoiceType { 
    public function configureOptions(OptionsResolver $resolver) { 
     parent::configureOptions($resolver); 

     $resolver->setDefault("choice_attr", function() { return ["class" => ""]; }); 
    } 
} 

Я не чувствую, как рефакторинг всех мои существующих форм, чтобы использовать этот новый тип, так что вместо этого я решил заменить Symfony предоставленного типа выбора с моим. Это может быть достигнуто путем изменения конфигурации службы для типа формы choice. Для этого я создал компилятор для моего пакета.

Дальнейшее чтение: Creating a Compiler Pass

namespace MyBundle\DependencyInjection\Compiler; 

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 
use Symfony\Component\DependencyInjection\ContainerBuilder; 

class MyCompilerPass implements CompilerPassInterface 
{ 
    public function process(ContainerBuilder $container) 
    { 
     $definition = $container->getDefinition("form.type.choice"); 
     $definition->setClass('MyBundle\Form\ChoiceNoOptAttrType'); 
    } 
} 

Теперь все, что осталось сделать, это зарегистрировать проход компилятора в пачке.

Дальнейшее чтение: How to Work with Compiler Passes in Bundles

namespace MyBundle; 

use Symfony\Component\DependencyInjection\ContainerBuilder; 
use Symfony\Component\HttpKernel\Bundle\Bundle; 
use MyBundle\DependencyInjection\Compiler\MyCompilerPass; 

class MyBundle extends Bundle 
{ 
    public function build(ContainerBuilder $container) 
    { 
     parent::build($container); 

     $container->addCompilerPass(new MyCompilerPass()); 
    } 
} 

И это он. Теперь все мои поля choice используют мой собственный класс, который гарантирует, что класс CSS, установленный в attr, не распространяется на мои <option> элементов.

1

Возможно, будет более простое решение, но вы можете взглянуть на Form Themes. Переопределите шаблон для choice_widget_options, чтобы классы не применялись к тегам опций.

{%- block choice_widget_options -%} 
    {% for group_label, choice in options %} 
     {%- if choice is iterable -%} 
      <optgroup label="{{ choice_translation_domain is sameas(false) ? group_label : group_label|trans({}, choice_translation_domain) }}"> 
       {% set options = choice %} 
       {{- block('choice_widget_options') -}} 
      </optgroup> 
     {%- else -%} 
      {% set attr = choice.attr %} 
      <option value="{{ choice.value }}" {# DELETE THIS PART: {{ block('attributes') }}#}{% if choice is selectedchoice(value) %} selected="selected"{% endif %}>{{ choice_translation_domain is sameas(false) ? choice.label : choice.label|trans({}, choice_translation_domain) }}</option> 
     {%- endif -%} 
    {% endfor %} 
{%- endblock choice_widget_options -%} 
+0

OP не использует шаблоны ветви. – user2268997

+0

Тогда вы можете переопределить шаблон php, я думаю. – LorenzSchaef

+0

Спасибо, это тоже пришло мне в голову. Однако я надеялся, что смогу переписать этот (довольно сложный) шаблон, поскольку мне, вероятно, придется его поддерживать для будущих версий. – Chris