Как я могу избежать применения attr ко всем параметрам поля моего выбора?
До Symfony 2.7 значение attr
для поля choice
применялось только к самому полю, т. е. к отображаемому элементу <select>
. Я использовал это, чтобы применить классы к этому элементу для его стилизации.
В Symfony 2.7 это поведение было изменено. Теперь все <option>
дочерние элементы элемента <select>
также получают одинаковые атрибуты ( фиксация изменения ) и, следовательно, классы.
Для некоторого пояснения пусть это будет код:
<?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, поэтому я не могу легко изменить их определение.
Какой самый простой способ избежать применения этих классов ко всем <option>
элементам поля choice
, все еще применяя его к элементу <select>
?
2 answers
Благодаря комментарию о choice_attr
@user2268997 я нашел соответствующее сообщение в блоге Новое в Symfony 2.7: Рефакторизация типа формы выбора , в котором подробно описывается использование (на данный момент недокументированного) 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
. Для этого я создал пропуск компилятора для своего пакета.
Дальнейшее чтение: Создание прохода компилятора
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');
}
}
Теперь все, что осталось сделать, это зарегистрировать пропуск компилятора в пакете.
Дальнейшее чтение: Как работать с Компилятор проходит в пакетах
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>
.
Может быть более простое решение, но вы можете взглянуть на Темы форм. Переопределите шаблон для 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 -%}