Skip to content

Commit c1befad

Browse files
committed
minor #1702 [LiveComponent] Document how to use extra_options in the Ajax-powered autocomplete (jakubtobiasz)
This PR was merged into the 2.x branch. Discussion ---------- [LiveComponent] Document how to use `extra_options` in the Ajax-powered autocomplete | Q | A | ------------- | --- | Bug fix? | no | New feature? | n/a | Issues | requested in #1322 (comment) | License | MIT > Time spent on this PR has been sponsored by [Commerce Weavers](https://commerceweavers.com/) ♥️. Commits ------- aab8892 [LiveComponent] Document how to use `extra_options` in the Ajax-powered autocomplete
2 parents 2e9bece + aab8892 commit c1befad

File tree

1 file changed

+166
-1
lines changed

1 file changed

+166
-1
lines changed

src/Autocomplete/doc/index.rst

+166-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ After creating this class, use it in your form:
167167

168168
Avoid passing any options to the 3rd argument of the ``->add()`` method as
169169
these won't be used during the Ajax call to fetch results. Instead, include
170-
all options inside the custom class (``FoodAutocompleteField``).
170+
all options inside the custom class (``FoodAutocompleteField``) or pass them as
171+
:ref:`extra options <passing-extra-options-to-the-ajax-powered-autocomplete>`.
171172

172173
Congratulations! Your ``EntityType`` is now Ajax-powered!
173174

@@ -274,6 +275,114 @@ to the options above, you can also pass:
274275
Set to ``focus`` to call the ``load`` function when control receives focus.
275276
Set to ``true`` to call the ``load`` upon control initialization (with an empty search).
276277

278+
``extra_options`` (default ``[]``)
279+
Allow you to pass extra options for Ajax-based autocomplete fields.
280+
281+
.. _passing-extra-options-to-the-ajax-powered-autocomplete:
282+
283+
Passing Extra Options to the Ajax-powered Autocomplete
284+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
285+
286+
.. versionadded:: 2.14
287+
288+
The ability to pass extra options was added in Autocomplete 2.14.
289+
290+
Autocomplete field options are not preserved when the field is rendered on an Ajax call. So, features like exclude some options
291+
based on the current form data are not possible by default. To partially avoid this limitation, the `extra_options` option was added.
292+
293+
::warning
294+
295+
Only scalar values (`string`, `integer`, `float`, `boolean`), `null` and `arrays` (consisted from the same types as mentioned before) can be passed as extra options.
296+
297+
Considering the following example, when the form type is rendered for the first time, it will use the `query_builder` defined
298+
while adding a `food` field to the `FoodForm`. However, when the Ajax is used to fetch the results, on the consequent renders,
299+
the default `query_builder` will be used::
300+
301+
// src/Form/FoodForm.php
302+
// ...
303+
304+
class FoodForm extends AbstractType
305+
{
306+
public function buildForm(FormBuilderInterface $builder, array $options): void
307+
{
308+
$currentFoodId = $builder->getData()->getId();
309+
310+
$builder
311+
->add('food', FoodAutocompleteField::class, [
312+
'query_builder' => function (EntityRepository $er) {
313+
$qb = $er->createQueryBuilder('o');
314+
315+
$qb->andWhere($qb->expr()->notIn('o.id', [$currentFoodId]));
316+
317+
return $qb;
318+
};
319+
}
320+
])
321+
;
322+
}
323+
}
324+
325+
If some food can be consisted of other foods, we might want to exclude the "root" food from the list of available foods.
326+
To achieve this, we can remove the `query_builder` option from the above example and pass the `excluded_foods` extra option
327+
to the `FoodAutocompleteField`::
328+
329+
// src/Form/FoodForm.php
330+
// ...
331+
332+
class FoodForm extends AbstractType
333+
{
334+
public function buildForm(FormBuilderInterface $builder, array $options): void
335+
{
336+
$currentFoodId = $builder->getData()->getId();
337+
338+
$builder
339+
->add('food', FoodAutocompleteField::class, [
340+
'extra_options' => [
341+
'excluded_foods' => [$currentFoodId],
342+
],
343+
)
344+
;
345+
}
346+
}
347+
348+
The magic of the `extra_options` is that it will be passed to the `FoodAutocompleteField` every time an Ajax call is made.
349+
So now, we can just use the `excluded_foods` extra option in the default `query_builder` of the `FoodAutocompleteField`::
350+
351+
// src/Form/FoodAutocompleteField.php
352+
// ...
353+
354+
use Symfony\Bundle\SecurityBundle\Security;
355+
use Symfony\UX\Autocomplete\Form\AsEntityAutocompleteField;
356+
use Symfony\UX\Autocomplete\Form\BaseEntityAutocompleteType;
357+
358+
#[AsEntityAutocompleteField]
359+
class FoodAutocompleteField extends AbstractType
360+
{
361+
public function configureOptions(OptionsResolver $resolver): void
362+
{
363+
$resolver->setDefaults([
364+
// ...
365+
'query_builder' => function (Options $options) {
366+
return function (EntityRepository $er) use ($options) {
367+
$qb = $er->createQueryBuilder('o');
368+
369+
$excludedFoods = $options['extra_options']['excluded_foods'] ?? [];
370+
if ([] !== $excludedFoods) {
371+
$qb->andWhere($qb->expr()->notIn('o.id', $excludedFoods));
372+
}
373+
374+
return $qb;
375+
};
376+
}
377+
]);
378+
}
379+
380+
public function getParent(): string
381+
{
382+
return BaseEntityAutocompleteType::class;
383+
}
384+
}
385+
277386
Using with a TextType Field
278387
---------------------------
279388

@@ -481,6 +590,62 @@ the ``ux_entity_autocomplete`` route and ``alias`` route wildcard:
481590
Usually, you'll pass this URL to the Stimulus controller, which is
482591
discussed in the next section.
483592

593+
Passing Extra Options to the Autocompleter
594+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
595+
596+
.. versionadded:: 2.14
597+
598+
The ability to pass extra options was added in Autocomplete 2.14.
599+
600+
If you need to pass extra options to the autocompleter, you can do so by implementing the
601+
``\Symfony\UX\Autocomplete\OptionsAwareEntityAutocompleterInterface`` interface.
602+
603+
.. tip::
604+
605+
If you want to know **why** you might need to use the `extra options` feature, see :ref:`passing-extra-options-to-the-ajax-powered-autocomplete`.
606+
607+
.. code-block:: diff
608+
609+
use Doctrine\ORM\EntityRepository;
610+
use Doctrine\ORM\QueryBuilder;
611+
use Sylius\Component\Product\Model\ProductAttributeInterface;
612+
use Symfony\Bundle\SecurityBundle\Security;
613+
use Symfony\UX\Autocomplete\OptionsAwareEntityAutocompleterInterface;
614+
615+
#[AutoconfigureTag('ux.entity_autocompleter', ['alias' => 'food'])]
616+
class FoodAutocompleter implements OptionsAwareEntityAutocompleterInterface
617+
{
618+
+ /**
619+
+ * @var array<string, mixed>
620+
+ */
621+
+ private array $options = [];
622+
623+
// ...
624+
625+
+ public function createFilteredQueryBuilder(EntityRepository $repository, string $query): QueryBuilder
626+
+ {
627+
+ $excludedFoods = $this->options['extra_options']['excluded_foods'] ?? [];
628+
+
629+
+ $qb = $repository->createQueryBuilder('o');
630+
+
631+
+ if ($productAttributesToBeExcluded !== []) {
632+
+ $qb
633+
+ ->andWhere($qb->expr()->notIn('o.id', $excludedFoods));
634+
+ ->setParameter('excludedFoods', $excludedFoods)
635+
+ ;
636+
+ }
637+
+
638+
+ return $qb;
639+
+ }
640+
641+
+/**
642+
+ * @param array<string, mixed> $options
643+
+ */
644+
+public function setOptions(array $options): void
645+
+{
646+
+ $this->options = $options;
647+
+}
648+
484649
.. _manual-stimulus-controller:
485650

486651
Manually using the Stimulus Controller

0 commit comments

Comments
 (0)