Skip to content

Commit f57c765

Browse files
committed
[LiveComponent] Document how to use extra_options in the Ajax-powered autocomplete
1 parent 71412bc commit f57c765

File tree

1 file changed

+162
-0
lines changed

1 file changed

+162
-0
lines changed

src/Autocomplete/doc/index.rst

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,109 @@ After creating this class, use it in your form:
171171

172172
Congratulations! Your ``EntityType`` is now Ajax-powered!
173173

174+
.. _passing-extra-options-to-the-ajax-powered-autocomplete:
175+
176+
Passing Extra Options to the Ajax-powered Autocomplete
177+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
178+
179+
.. versionadded:: 2.13
180+
181+
The ability to pass extra options was added in LiveComponents 2.13.
182+
183+
By default, when you pass any options while adding a field to a form type, they will be lost
184+
when the autocomplete field is rendered on an Ajax call. To partially avoid this limitation,
185+
the `extra_options` option was added. It can only hold a scalar values, but it covers most use cases.
186+
187+
Considering the following example, when the form type is rendered for the first time, it will use the `query_builder` defined
188+
while adding a `food` field to the `FoodForm`. However, when the Ajax is used to fetch the results, on the consequent renders,
189+
the default `query_builder` will be used::
190+
191+
// src/Form/FoodForm.php
192+
// ...
193+
194+
class FoodForm extends AbstractType
195+
{
196+
public function buildForm(FormBuilderInterface $builder, array $options): void
197+
{
198+
$currentFoodId = $builder->getData()->getId();
199+
200+
$builder
201+
->add('food', FoodAutocompleteField::class, [
202+
'query_builder' => function (EntityRepository $er) {
203+
$qb = $er->createQueryBuilder('o');
204+
205+
$qb->andWhere($qb->expr()->notIn('o.id', [$currentFoodId]));
206+
207+
return $qb;
208+
};
209+
}
210+
])
211+
;
212+
}
213+
}
214+
215+
If some food can be consisted of other foods, we might want to exclude the "root" food from the list of available foods.
216+
To achieve this, we can remove the `query_builder` option from the above example and pass the `excluded_foods` extra option
217+
to the `FoodAutocompleteField`::
218+
219+
// src/Form/FoodForm.php
220+
// ...
221+
222+
class FoodForm extends AbstractType
223+
{
224+
public function buildForm(FormBuilderInterface $builder, array $options): void
225+
{
226+
$currentFoodId = $builder->getData()->getId();
227+
228+
$builder
229+
->add('food', FoodAutocompleteField::class, [
230+
'extra_options' => [
231+
'excluded_foods' => [$currentFoodId],
232+
],
233+
)
234+
;
235+
}
236+
}
237+
238+
The magic of the `extra_options` option is that it will be passed to the `FoodAutocompleteField` every time an Ajax call is made.
239+
So now, we can just use the `excluded_foods` extra option in the default `query_builder` of the `FoodAutocompleteField`::
240+
241+
// src/Form/FoodAutocompleteField.php
242+
// ...
243+
244+
use Symfony\Bundle\SecurityBundle\Security;
245+
use Symfony\UX\Autocomplete\Form\AsEntityAutocompleteField;
246+
use Symfony\UX\Autocomplete\Form\BaseEntityAutocompleteType;
247+
248+
#[AsEntityAutocompleteField]
249+
class FoodAutocompleteField extends AbstractType
250+
{
251+
public function configureOptions(OptionsResolver $resolver)
252+
{
253+
$resolver->setDefaults([
254+
// ...
255+
'query_builder' => function (Options $options) {
256+
return function (EntityRepository $er) use ($options) {
257+
$qb = $er->createQueryBuilder('o');
258+
259+
$excludedFoods = $options['extra_options']['excluded_foods'] ?? [];
260+
if ([] !== $excludedFoods) {
261+
$qb->andWhere($qb->expr()->notIn('o.id', $excludedFoods));
262+
}
263+
264+
return $qb;
265+
};
266+
}
267+
]);
268+
}
269+
270+
public function getParent(): string
271+
{
272+
return BaseEntityAutocompleteType::class;
273+
}
274+
}
275+
276+
174277
Styling Tom Select
175278
------------------
176279

@@ -274,6 +377,9 @@ to the options above, you can also pass:
274377
Set to ``focus`` to call the ``load`` function when control receives focus.
275378
Set to ``true`` to call the ``load`` upon control initialization (with an empty search).
276379

380+
``extra_options`` (default ``[]``)
381+
Allow you to pass extra options for Ajax-based autocomplete fields.
382+
277383
Using with a TextType Field
278384
---------------------------
279385

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

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

486648
Manually using the Stimulus Controller

0 commit comments

Comments
 (0)