我需要实现像https://select2.github.io/examples.html#tags这样的标签输入。我不太确定如何使用symfony / form实现服务器端部分。我无法找到任何现有的实现,我不确定是否应该尝试以某种方式破解ChoiceType或尝试单独实现它。
你能帮帮我吗?我错过了任何现有的解决方案吗?答案 0 :(得分:1)
IMO这种模式非常适合CollectionType。 How to Embed a Collection of Forms也可能很有趣。您只需要确保您的javascript / jquery插件可以与原型功能一起使用。
答案 1 :(得分:1)
<强> 1。创建一个继承自ChoiceType的特殊FormType:
namespace Alsatian\FormBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
class ExtentedChoiceType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array('choices'=>array()));
}
public function getParent()
{
return ChoiceType::class;
}
}
<强> 2。创建一个FormEvent订阅者,使用您在客户端添加的标记填充表单:
namespace Alsatian\FormBundle\Form\Extensions;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Alsatian\FormBundle\Form\ExtentedChoiceType;
class ExtentedChoicesSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
FormEvents::PRE_SET_DATA => array('populateChoices',-50),
FormEvents::PRE_SUBMIT => array('populateChoices',-50)
);
}
public function populateChoices(FormEvent $event)
{
foreach($event->getForm()->all() as $child){
if(get_class($child->getConfig()->getType()->getInnerType()) === ExtentedChoiceType::class){
$this->populateChoice($event,$child->getName());
}
}
}
private function populateChoice(FormEvent $event,$childName)
{
$form = $event->getForm();
$child = $form->get($childName);
$type = get_class($child->getConfig()->getType()->getInnerType());
$options = $child->getConfig()->getOptions();
$choices = array();
$data = $event->getData();
if(!array_key_exists($childName,$data)){return;}
$data = $data[$childName];
if($data != null){
if(is_array($data)){
foreach($data as $choice){
$choices[$choice] = $choice;
}
}
else{
$choices[$data] = $data;
}
}
// Feel free to find a better way to reuse the defined options. In Sf 2.6 it was not possible here :
$newOptions = array('route'=>$options['route'],'required'=>$options['required'],'multiple'=>$options['multiple'],'choices'=>$choices);
$form->add($childName,$type,$newOptions);
}
}
第3。用法:
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Alsatian\FormBundle\Form\ExtentedChoiceType;
use Alsatian\FormBundle\Form\Extensions\ExtentedChoicesSubscriber;
class ArticleType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('tags',ExtentedChoiceType::class,array("multiple"=>true))
->addEventSubscriber(new ExtentedChoicesSubscriber());
}
}
答案 2 :(得分:0)
我确定有很多方法可以做到这一点。例如,这是我在项目中使用它的方式。
此自定义表单类型在从视图转换为模型数据时返回array
,并且在从模型转换为视图数据时需要array
。
有关Symfony3中数据转换器的更多信息:http://symfony.com/doc/current/cookbook/form/data_transformers.html#about-model-and-view-transformers
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\FormBuilderInterface;
class TagsType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addViewTransformer(new CallbackTransformer(
function ($original) {
return $original ? implode(', ', $original) : '';
},
function ($submitted) {
if (!$submitted) {
return [];
}
$submitted = array_map(function($tag) {
return trim($tag);
}, explode(',', $submitted));
return $submitted;
}
));
}
public function getName()
{
return 'tags';
}
public function getParent()
{
return 'text';
}
}
根据您的前端部分,默认情况下会将其渲染为输入字段,您可以在其中编写以逗号分隔的标记。