2015-08-17 10 views
5

Prima di Symfony 2.7, il valore attr per un campo choice si applicava solo al campo stesso, ovvero l'elemento <select> reso. L'ho usato per applicare le classi a questo elemento per modellarlo.Come posso evitare di applicare attr a tutte le opzioni del mio campo di scelta?

In Symfony 2.7 questo comportamento è stato modificato. Ora, tutti i <option> figli dell'elemento <select> ottengono anche gli stessi attributi (commit of the change) e quindi le classi.


Per qualche chiarimento, lasciate che questo sia il codice:

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

Allora questo è l'uscita di 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> 

E questa è l'uscita di 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> 

Le classi che si applicano non sono adatte per gli elementi <option> poiché definiscono i bordi e simili per il campo effettivo. Si noti che queste sono classi definite dall'interfaccia utente jQuery, quindi non posso modificare facilmente la loro definizione.

Qual è il modo più semplice per evitare l'applicazione di queste classi a tutti <option> elementi di un campo choice pur applicandolo all'elemento <select>?

+0

Forse è un bug. Perché non ho trovato nulla nel UPGRADE-2.7 per questo, e quella funzionalità è ciò che l'opzione ''choice_attr'' dovrebbe fare. – user2268997

+0

@ user2268997 Sì, non sono riuscito a trovare nulla, ma guardando il codice che ho collegato sembra molto intenzionale per me. – Chris

+0

Non conosco molto i modelli di PHP, ma sembra che 'attr' sia risolto da '$ choice' che è un'istanza' ChoiceView', creata da 'DefaultChoiceListFactory', e viene passata l'opzione' choice_attr' a quella fabbrica nella definizione 'ChoiceType'. (se non si fornisce il proprio' ChoiceList' ofcourse) – user2268997

risposta

4

Grazie al commento circa choice_attr da @ user2268997 ho trovato il relativo post New in Symfony 2.7: Choice form type refactorization che dettaglia l'uso della (fin d'ora privi di documenti) choice_attr opzione.

Sembra che Symfony unisca gli attributi in choice_attr con quelli in attr durante il rendering del campo. Ciò significa che è necessario sovrascrivere l'attributo class in choice_attr.

Ho provato a farlo nel codice accanto a dove definisco lo attr ma non ho avuto fortuna. Sembra che tu debba farlo nella definizione del tipo di modulo. Ecco un estratto dal mio modulo dopo aver aggiunto l'opzione 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" => ""]; } 
      ]); 
} 

Il risultato è come avevo sperato. Probabilmente lo rifatterò anche al mio tipo di modulo personalizzato, quindi non ho bisogno di ripeterlo su tutto il mio pacchetto.


ho deciso di creare un tipo personalizzato choice con il comportamento desiderato sopra descritta e l'uso che uno in tutta la mia domanda.

Qui è il mio tipo scelta:

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" => ""]; }); 
    } 
} 

io non la pensano come il refactoring tutte le mie forme esistenti di utilizzare questo nuovo tipo, così invece ho optato per sostituire il tipo di scelta Symfony fornito con la mia. Ciò può essere ottenuto modificando la configurazione del servizio per il tipo di modulo choice. Per fare questo, ho creato un pass per compilatore per il mio pacchetto.

Ulteriori approfondimenti: 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'); 
    } 
} 

Ora tutto ciò che rimane da fare è registrare il passaggio del compilatore nel bundle.

Ulteriori approfondimenti: 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()); 
    } 
} 

Ed è proprio questo. Ora tutti i miei campi choice utilizzano la mia classe personalizzata che si assicura che la classe CSS impostata in attr non venga propagata ai miei elementi <option>.

1

Potrebbe esserci una soluzione più semplice, ma potrebbe essere utile dare un'occhiata a Form Themes. Sovrascrivere il modello per choice_widget_options in modo che le classi non vengano applicate ai tag di opzione.

{%- 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

L'OP non utilizza i modelli di ramoscello. – user2268997

+0

Quindi, è possibile sovrascrivere il modello PHP. – LorenzSchaef

+0

Grazie, mi è venuto in mente anche questo. Tuttavia, speravo di poter saltare la sovrascrittura di questo modello (piuttosto complesso) poiché probabilmente dovrei mantenerlo per le versioni future. – Chris

Problemi correlati