-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Form] Support "allow_add"/"allow_delete" in ChoiceType #9310
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
A big 👍 for
|
How would the added choice value be transformed to a model value though ? |
@stof, the same way you actually deal with |
That could work. However, automatic construction using the This might be a bit complicated to implement though. |
Any news about this feature ? |
@qferr It's very complicated. See https://speakerdeck.com/heahdude/symfony-forms-use-cases-and-optimization for an example of a custom entity type handling creation by extending the I'll try to work on a global implementation for 3.3 :) |
Is this still a planned feature? Would be great for e.g. tagging, etc. and js tools like Selectize.js or similar others could be deployed a lot easier |
We do not have any "planned features" in Symfony. Each issue labelled as a feature is just a wish of someone that could happen if someone volunteers to implement it. It seems that until today nobody needed this feature so badly that they created a pull request implementing it. |
Adding I propose to close. See also #23679 (i think we should close that one as well, and keep it as a feature) |
Personnally I consider it a very important feature for ChoiceType and EntityType. |
but if it can be solved using choice loaders already (e.g. in userland) it's not worth it IMO. we should investigate it :) |
For me public function buildView(FormView $view, FormInterface $form, array $options)
{
foreach ($this->getRange($options['vehicle_type']) as $value) {
$view->vars['choices'][] = new ChoiceView($value, $value, $value);
}
} |
@Daisuke-sama Can you give a more concrete example of what you want to achieve and isn't possible right now? |
@xabbuh in this issue, marked as duplicated of this one, I give an specific example, I don't know if this is what you ask for. |
@nzurita Sorry for the late reply. If I do not miss anything, this looks like a good example where one would simply populate (i.e. add) the choice type in a |
Hello @xabbuh , sorry for the late reply, I've been taken by other projects. What I wrote in that issue is that, when it comes to multilevel and dynamic select lists, for example, country > state/province > town, this event listener solution becomes quite unfriendly, and a pure ajax solution would be more desirable, |
I created a poor bundle to provide this feature for ChoiceType and EntityType. It recreate the type taking the pre-set-dat or pre-submit data as choices. It doesn't cover all use case, and it would be better to have an official built-in "allow_add" option. I understand, it would be difficult for the ChoiceType (model conversion). But for the EntityType it should be easier. And I think it is a very common task (in a database table, we often have thousands of entities, in this case it is often better to load them with Ajax calls, for example with the select2 ajax library). Despite of the low quality of my bundle, I can see it was installed more than 1000 times on packagist, and I often see stackoverflow question asking how to implement something like that. |
A very typical example is to bind a EntityType with the select2 Javscript library. Assuming you have an entity "City" binded with a cities table in the SQL database. This table has several millions of entries and we can't load all of them in the choices option. So we will start generating the entitytype with an emty choicelist. Using the select2 library, on the client side we can do Ajax calls to search available cities depending on a searched string. Once selected, select2 will add them to the HTML select élément and set them selected. The server will receive selected choices which were not in the initial choices. If there were any option "allow_add" it could simply add the selected entities to the availables choices if the submitted data can be converted innan entity. |
I've just had to deal with this so I figured I would chip in here. This got closed because there are ways of achieving the same goal currently. Ok, but I think we should look at the fact we're currently increasing complexity and in many cases, we're receiving no benefits. If I have a UI that is using a select for UI purposes and I want to dynamically add things to that select via user input, for whatever reason., I then need to have a workaround for the fact that the Symfony tool to help me with my form UI won't let me do that. My choices as I see them now. I can either add a new custom field type where I have a hidden text input and then a select UI that is not important for the form and then I dynamically add that. So now I've increased my code complexity by adding a new field type and also the JS logic so that when a select is changed the hidden field in my custom field type is populated. That's not a pleasant experience. Or I can add an event lister find the value in the data sent in the HTTP request, remove the choice field and re-add it with this new user provider option. Again, do-able but not pleasant. For me, the issue is more that we're adding more code that is fundamentally a code smell. For no benefit to us other than we continue to be able to use Symfony Forms. I personally think having the ability to do workarounds shouldn't be a reason in itself not to add a feature. Reading through the comments here it seems that the only issue here is not that actual feature but that no one has added it, but the pull request is labelled waiting for feedback, I personally wouldn't go implementing a feature that has an issue that is awaiting feedback. Maybe this is highlighting a possible improvement in Symfony issue workflow that could help with new people looking to contribute, maybe it would be an idea to add a new label "Waiting for a contributor" or something that tells people that this idea is acceptable and just needs someone to contribute a PR to implement the feature. That being said, if I worked on this would it be accepted? |
I am having exactly the same issue and find it incredible that it is not being taken seriously! |
[and yes, i know this thread is old ;)] Not sure if that's your case, but for some, below example may be enough (element presented as select field, submitted value will be dynamically added on PreSubmit event) $form = $this->createFormBuilder()
->add(child: 'SomeSelectField', type: ChoiceType::class, options: [
'choices' => [
'choice1' => 'value1',
'choice1' => 'value2',
'choice1' => 'value3'
],
])
->add(child: 'save', type: SubmitType::class, options: [
'label' => 'Try me'
])
->addEventListener(
FormEvents::PRE_SUBMIT,
function (PreSubmitEvent $event): void {
// fetch submitted value
$data = $event->getData()['SomeSelectField'];
$form = $event->getForm();
// retrieve original select field options, so we won't need to repeat them
$opts = $form->get('SomeSelectField')->getConfig()->getOptions();
// here we're adding our fetched submitted value to the list of select field options
$opts['choices'][$data] = $data;
// not sure if this is needed, but i like to leave it for clearity
$form->remove('SomeSelectField');
// add reconfigured (=with changed options) field
$form->add(child: 'SomeSelectField', type: ChoiceType::class, options: $opts);
}
)
->getForm(); |
Hello, the event system is working fine as long as you don't try to move the form field in his own custom type for reusability. I replaced the event system by a choice loader implementation and it is working fine : <?php
declare(strict_types=1);
namespace App\Observer\ProductLine\UI\Http\Web\Html\Form;
use App\Observer\ProductLine\Application\Query\ListAsSymfonyFormChoices\ListProductLinesAsSymfonyFormChoicesQuery;
use App\Observer\ProductLine\Application\Query\ListAsSymfonyFormChoices\ListProductLinesAsSymfonyFormChoicesViewModel;
use App\Observer\ProductLine\Domain\Entity\ValueObject\ProductLineId;
use App\Shared\Application\Bus\Query\QueryBus;
use App\Shared\Domain\Assert\Assert;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\ChoiceList\Loader\AbstractChoiceLoader;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Routing\RouterInterface;
class AutocompleteProductLineFormType extends AbstractType
{
public function __construct(private QueryBus $queryBus, private RouterInterface $router)
{
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'choice_loader' => new class($this->queryBus) extends AbstractChoiceLoader {
/**
* @var ProductLineId[]
*/
private array $productLineIds = [];
public function __construct(private readonly QueryBus $queryBus)
{
}
public function loadChoicesForValues(array $values, callable $value = null): array
{
$this->productLineIds = array_map(fn (string $id) => ProductLineId::fromInt((int) $id), $values);
return parent::loadChoicesForValues($values, $value);
}
public function loadValuesForChoices(array $choices, callable $value = null): array
{
Assert::allIsInstanceOf($choices, ProductLineId::class);
$this->productLineIds = $choices;
return parent::loadValuesForChoices($choices, $value);
}
/**
* @return iterable<ProductLineId>
*/
protected function loadChoices(): iterable
{
if ([] === $this->productLineIds) {
return [];
}
/** @var ListProductLinesAsSymfonyFormChoicesViewModel $choices */
$choices = $this->queryBus->ask(new ListProductLinesAsSymfonyFormChoicesQuery($this->productLineIds));
return $choices->choices;
}
},
'choice_value' => function (ProductLineId $productLineId) {
return $productLineId->value();
},
]);
}
public function buildView(FormView $view, FormInterface $form, array $options): void
{
$view->vars['attr']['data-ajax-url'] = $this->router->generate('tom_select_product_line_list');
}
public function getParent(): string
{
return ChoiceType::class;
}
} On a side note, I you need to display multiple times the same form type (I have a primary and secondary select list with the same data), you will need to extend this class (not using the same form type, nor doing sub custom form type referring to it using |
ChoiceType should support the options "allow_add" and "allow_delete" in the same fashion as CollectionType does.
The text was updated successfully, but these errors were encountered: