component - symfony using form
Symfony2: botones de radio en una colección (4)
En mi aplicación, creé un formulario usando el tipo de campo de collection
:
$builder->add(''tags'', ''collection'', array(
''type'' => new TagType(),
''label'' => false,
''allow_add'' => true,
''allow_delete'' => true,
''by_reference'' => false
));
Con algunos JQuery, este código funciona correctamente, pero ahora me gustaría seleccionar uno de esta etiqueta dinámica para que sea "la etiqueta principal".
En mi entidad Tag, agregué un atributo booleano que define si la etiqueta es la principal o no:
/**
* @ORM/Column(name="main", type="boolean")
*/
private $main;
Pero en mi opinión, cada fila ahora contiene una casilla de verificación. Entonces puedo seleccionar más de una etiqueta principal. ¿Cómo transformar esta casilla de verificación en el botón de opción, por favor?
Tal vez haya algo que ver con la opción de formulario múltiple , pero podría requerir un pequeño ajuste en su formulario de recopilación y entidad de etiqueta.
Lo primero que debes tener en cuenta es que en tu esquema si la etiqueta se convierte en principal para una entidad, será principal para todas las entidades porque el atributo de la tienda de etiquetas y algunas entidades pueden etiquetarse con una etiqueta.
Así que la decisión más simple es crear una nueva propiedad main_tag
cerca de las etiquetas en su entidad, crear el campo oculto main_tag
(con id para Etiquetar transformador de datos ) en su formulario y rellenar y cambiar este campo con jQuery (por ejemplo, configurarlo en tag clic o borrar en eliminar la etiqueta principal)
Esta no es la solución correcta, pero ya que está utilizando jQuery para agregar / eliminar ...
TagType
->add(''main'', ''radio'', [
''attr'' => [
''class'' => ''toggle''
],
''required'' => false
])
jQuery
div.on(''change'', ''input.toggle'', function() {
div
.find(''input.toggle'')
.not(this)
.removeAttr(''checked'');
});
http://jsfiddle.net/coma/CnvMk/
Y use una restricción de devolución de llamada para asegurarse de que solo haya una etiqueta principal.
No estás abordando el problema desde el ángulo correcto. Si debe haber una etiqueta principal, esta propiedad no se debe agregar a la entidad Tag , sino a la entidad que la contiene.
Estoy hablando de la entidad data_class relacionada con el formulario que tiene el atributo de etiquetas . Esta es la entidad que debería tener una propiedad mainTag .
Si se define correctamente, este nuevo atributo mainTag no será booleano, ya que contendrá una instancia de Tag y, por lo tanto, no se asociará a una entrada de casilla de verificación.
Entonces, como yo lo veo, debes tener una propiedad mainTag que contenga tu instancia y una propiedad de etiquetas que concuerde con todas las otras etiquetas.
El problema con eso es que su campo de colección ya no contendrá la etiqueta principal. Por lo tanto, también debe crear un getter getAllTags especial que fusionará su etiqueta principal con todos los demás, y cambie la definición de su colección a:
$builder->add(''allTags'', ''collection'', array(
''type'' => new TagType(),
''label'' => false,
''allow_add'' => true,
''allow_delete'' => true,
''by_reference'' => false
));
Ahora, ¿cómo agregamos las cajas de radio, puede preguntar? Para esto, tendrás que generar un nuevo campo:
$builder->add(''mainTag'', ''radio'', array(
''type'' => ''choice'',
''multiple'' => false,
''expanded'' => true,
''property_path'' => ''mainTag.id'', // Necessary, for ''choice'' does not support data_classes
));
Estos son los conceptos básicos, sin embargo, solo se vuelve más complejo a partir de aquí. El verdadero problema aquí es cómo se muestra su formulario. En un mismo campo, mezcla la visualización habitual de una colección y la visualización de un campo de elección de la forma principal de esa colección. Esto te obligará a usar el tematizado de formas .
Para permitir cierto espacio para la reutilización, debe crear un campo personalizado. La clase de datos asociada:
class TagSelection
{
private mainTag;
private $tags;
public function getAllTags()
{
return array_merge(array($this->getMainTag()), $this->getTags());
}
public function setAllTags($tags)
{
// If the main tag is not null, search and remove it before calling setTags($tags)
}
// Getters, setters
}
El tipo de formulario:
class TagSelectionType extends AbstractType
{
protected buildForm( ... )
{
$builder->add(''allTags'', ''collection'', array(
''type'' => new TagType(),
''label'' => false,
''allow_add'' => true,
''allow_delete'' => true,
''by_reference'' => false
));
// Since we cannot know which tags are available before binding or setting data, a listener must be used
$formFactory = $builder->getFormFactory();
$listener = function(FormEvent $event) use ($formFactory) {
$data = $event->getForm()->getData();
// Get all tags id currently in the data
$choices = ...;
// Careful, in PRE_BIND this is an array of scalars while in PRE_SET_DATA it is an array of Tag instances
$field = $this->factory->createNamed(''mainTag'', ''radio'', null, array(
''type'' => ''choice'',
''multiple'' => false,
''expanded'' => true,
''choices'' => $choices,
''property_path'' => ''mainTag.id'',
));
$event->getForm()->add($field);
}
$builder->addEventListener(FormEvent::PRE_SET_DATA, $listener);
$builder->addEventListener(FormEvent::PRE_BIND, $listener);
}
public function getName()
{
return ''tag_selection'';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
''data_class'' => ''TagSelection'', // Adapt depending on class name
// ''prototype'' => true,
));
}
}
Finalmente, en la plantilla de tema de formulario:
{% block tag_selection_widget %}
{% spaceless %}
{# {% set attr = attr|default({})|merge({''data-prototype'': form_widget(prototype)}) %} #}
<ul {{ block(''widget_attributes'') }}>
{% for child in form.allTags %}
<li>{{ form_widget(form.mainTag[child.name]) }} {{ form_widget(child) }}</li>
{% endfor %}
</ul>
{% endspaceless %}
{% endblock tag_selection_widget %}
Por último, debemos incluir eso en su entidad matriz, la que originalmente contenía las etiquetas :
class entity
{
// Doctrine definition and whatnot
private $tags;
// Doctrine definition and whatnot
private $mainTag;
...
public setAllTags($tagSelection)
{
$this->setMainTag($tagSelection->getMainTag());
$this->setTags($tagSelection->getTags());
}
public getAllTags()
{
$ret = new TagSelection();
$ret->setMainTag($this->getMainTag());
$ret->setTags($this->getTags());
return $ret;
}
...
}
Y en tu forma original:
$builder->add(''allTags'', new TagSelection(), array(
''label'' => false,
));
Reconozco que la solución que propongo es detallada, sin embargo, me parece que es la más eficiente. Lo que estás tratando de hacer no se puede hacer fácilmente en Symfony.
También puede observar que hay una opción de "prototipo" impar en el comentario. Solo quería subrayar una propiedad de "colección" muy útil en su caso: la opción de prototipo contiene un elemento en blanco de su colección, con marcadores de posición para reemplazar. Esto permite agregar rápidamente nuevos elementos en un campo de colección usando javascript, más información aquí .