Skip to content

Commit

Permalink
[LiveComponent] Document how to use extra_options in the Ajax-power…
Browse files Browse the repository at this point in the history
…ed autocomplete
  • Loading branch information
jakubtobiasz committed Apr 9, 2024
1 parent 71412bc commit a06528b
Showing 1 changed file with 168 additions and 0 deletions.
168 changes: 168 additions & 0 deletions src/Autocomplete/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,115 @@ After creating this class, use it in your form:

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

.. _passing-extra-options-to-the-ajax-powered-autocomplete:

Passing Extra Options to the Ajax-powered Autocomplete
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. versionadded:: 2.13

The ability to pass extra options was added in LiveComponents 2.13.

By default, when you pass any options while adding a field to a form type, they will be lost
when the autocomplete field is rendered on an Ajax call. To partially avoid this limitation,
the `extra_options` option was added. It can only hold a scalar values, but it covers most use cases.

Considering the following example, when the form type is rendered for the first time, it will use the `query_builder` defined
while adding a `food` field to the `FoodForm`. However, when the Ajax is used to fetch the results, on the consequent renders,
the default `query_builder` will be used.

.. code-block:: php

Check failure on line 191 in src/Autocomplete/doc/index.rst

View workflow job for this annotation

GitHub Actions / DOCtor-RST

Please do not use ".. code-block:: php", use "::" instead.
// src/Form/FoodForm.php
// ...
class FoodForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$currentFoodId = $builder->getData()->getId();
$builder
->add('food', FoodAutocompleteField::class, [
'query_builder' => function (EntityRepository $er) {
$qb = $er->createQueryBuilder('o');
$qb->andWhere($qb->expr()->notIn('o.id', [$currentFoodId]));
return $qb;
};
}
])
;
}
}
If some food can be consisted of other foods, we might want to exclude the "root" food from the list of available foods.
To achieve this, we can remove the `query_builder` option from the above example and pass the `excluded_foods` extra option
to the `FoodAutocompleteField`:

.. code-block:: php

Check failure on line 221 in src/Autocomplete/doc/index.rst

View workflow job for this annotation

GitHub Actions / DOCtor-RST

Please do not use ".. code-block:: php", use "::" instead.
// src/Form/FoodForm.php
// ...
class FoodForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$currentFoodId = $builder->getData()->getId();
$builder
->add('food', FoodAutocompleteField::class, [
'extra_options' => [
'excluded_foods' => [$currentFoodId],
],
)
;
}
}
The magic of the `extra_options` option is that it will be passed to the `FoodAutocompleteField` every time an Ajax call is made.
So now, we can just use the `excluded_foods` extra option in the default `query_builder` of the `FoodAutocompleteField`:

.. code-block:: php

Check failure on line 245 in src/Autocomplete/doc/index.rst

View workflow job for this annotation

GitHub Actions / DOCtor-RST

Please do not use ".. code-block:: php", use "::" instead.
// src/Form/FoodAutocompleteField.php
// ...
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\UX\Autocomplete\Form\AsEntityAutocompleteField;
use Symfony\UX\Autocomplete\Form\BaseEntityAutocompleteType;
#[AsEntityAutocompleteField]
class FoodAutocompleteField extends AbstractType
{
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
// ...
'query_builder' => function (Options $options) {
return function (EntityRepository $er) use ($options) {
$qb = $er->createQueryBuilder('o');
$excludedFoods = $options['extra_options']['excluded_foods'] ?? [];
if ([] !== $excludedFoods) {
$qb->andWhere($qb->expr()->notIn('o.id', $excludedFoods));
}
return $qb;
};
}
]);
}
public function getParent(): string
{
return BaseEntityAutocompleteType::class;
}
}
Styling Tom Select
------------------

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

``extra_options`` (default ``[]``)
Allow you to pass extra options for Ajax-based autocomplete fields.

Using with a TextType Field
---------------------------

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

Passing Extra Options to the Autocompleter

Check failure on line 596 in src/Autocomplete/doc/index.rst

View workflow job for this annotation

GitHub Actions / DOCtor-RST

Please ensure title "Passing Extra Options to the Autocompleter" and underline length are matching
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. versionadded:: 2.13

The ability to pass extra options was added in LiveComponents 2.13.

If you need to pass extra options to the autocompleter, you can do so by implementing the
``\Symfony\UX\Autocomplete\OptionsAwareEntityAutocompleterInterface`` interface.

.. tip::

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`.

.. code-block:: diff
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Sylius\Component\Product\Model\ProductAttributeInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\UX\Autocomplete\OptionsAwareEntityAutocompleterInterface;
#[AutoconfigureTag('ux.entity_autocompleter', ['alias' => 'food'])]
class FoodAutocompleter implements OptionsAwareEntityAutocompleterInterface
{
+ /**
+ * @var array<string, mixed>
+ */
+ private array $options = [];
// ...
+ public function createFilteredQueryBuilder(EntityRepository $repository, string $query): QueryBuilder
+ {
+ $excludedFoods = $this->options['extra_options']['excluded_foods'] ?? [];
+
+ $qb = $repository->createQueryBuilder('o');
+
+ if ($productAttributesToBeExcluded !== []) {
+ $qb
+ ->andWhere($qb->expr()->notIn('o.id', $excludedFoods));
+ ->setParameter('excludedFoods', $excludedFoods)
+ ;
+ }
+
+ return $qb;
+ }
+/**
+ * @param array<string, mixed> $options
+ */
+public function setOptions(array $options): void
+{
+ $this->options = $options;
+}
.. _manual-stimulus-controller:

Manually using the Stimulus Controller
Expand Down

0 comments on commit a06528b

Please sign in to comment.