symfony / ux

Symfony UX initiative: a JavaScript ecosystem for Symfony

Home Page:https://ux.symfony.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Autocomplete][LiveComponent] Change not detected when using autocomplete_url in a dynamic form

antoniovj1 opened this issue · comments

I have a dynamic form with autocomplete functionality. The issue starts when I try to change the URL for autocompletion. I can see that the HTML changes, but Tom Select is not updated and is still querying the old URL.

When I use autocomplete with EntityType::class, it just works, but it's not useful in this case.

I think the main problem lies in the initialization of the mutationObserver in the autocomplete controller.

 initializeTomSelect() {
        if (this.selectElement) {
            this.selectElement.setAttribute('data-skip-morph', '');
        }
        if (this.urlValue) {
            this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocompleteWithRemoteData).call(this, this.urlValue, this.hasMinCharactersValue ? this.minCharactersValue : null);
            return;
        }
        if (this.optionsAsHtmlValue) {
            this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocompleteWithHtmlContents).call(this);
            return;
        }
        this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocomplete).call(this);
        this.startMutationObserver();
    }

Because if we enter the this.urlValue, the observer is not started.

Here its my code:

 public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('typeId', EnumType::class, [
                'class' => RuleType::class,
            ])
            ->add('operator', EnumType::class, [
                'class' => QueryOperator::class,
            ])
            ->add('nextNodeOperator', EnumType::class, [
                'class' => JoinOperator::class,
            ]);

        $formModifier = function (FormInterface $form, ?RuleType $ruleType = null): void {
            if ($ruleType === null) {
                $ruleType = RuleType::TYPE_FAMILY;
            }

            if ($ruleType === RuleType::TYPE_FAMILY) {
                $route = $this->router->generate('app_entrypoint_api_search_by_family');
            }

            if ($ruleType === RuleType::TYPE_BRAND) {
                $route = $this->router->generate('app_entrypoint_api_search_by_brand');
            }

            if ($ruleType === RuleType::TYPE_ATTRIBUTE) {
                $route = $this->router->generate('app_entrypoint_api_search_by_attribute');
            }

            $form->add('values', ChoiceType::class, [
                'autocomplete' => true,
                'autocomplete_url' => $route,
                'multiple' => true,
            ]);
        };

        $builder->addEventListener(
            FormEvents::PRE_SET_DATA,
            function (FormEvent $event) use ($formModifier): void {
                $data = $event->getData();
                $formModifier($event->getForm(), $data?->typeId());
            }
        );

        $builder->get('typeId')->addEventListener(
            FormEvents::POST_SUBMIT,
            function (FormEvent $event) use ($formModifier): void {
                $ruleType = $event->getForm()->getData();
                $formModifier($event->getForm()->getParent(), $ruleType);
            }
        );
    }

It might be a bug in my code, but I've tried everything I could think of, and seeing with using an Entity instead of a URL works fine, I believe the problem must be related to what I've commented on.

Thanks.

The issue starts when I try to change the URL for autocompletion

When you say "dynamic" form you're talking about those Event/listeners, or something else ? Is the rendered HTML correct ?

Yes I'm talking about Event/Listeners, but I'm using also LiveComponents in the form (I'm not sure if is causing the issue)

The HTML looks fine for me, but the Tom Select behaviour is not ok. It starts using de TYPE_FAMILY route and when I change the type I can see de data attributes correctly updated.

But the autocomplete still using the FAMILY route.

This is the HTML at first:

<div class="col-3">
    <div>
        <label for="segment_form_filters_0_rules_0_values-ts-control" class="required" id="segment_form_filters_0_rules_0_values-ts-label">Valor</label>
        <select
            id="segment_form_filters_0_rules_0_values"
            name="segment_form[filters][0][rules][0][values][]"
            required="required"
            data-controller="symfony--ux-autocomplete--autocomplete"
            data-symfony--ux-autocomplete--autocomplete-url-value="/api/v1/search/family"
            data-symfony--ux-autocomplete--autocomplete-max-results-value="10"
            data-symfony--ux-autocomplete--autocomplete-loading-more-text-value="Loading more results..."
            data-symfony--ux-autocomplete--autocomplete-no-results-found-text-value="No results found"
            data-symfony--ux-autocomplete--autocomplete-no-more-results-text-value="No more results"
            data-symfony--ux-autocomplete--autocomplete-preload-value="focus"
            class="form-control form-control-solid tomselected ts-hidden-accessible"
            multiple="multiple"
            data-skip-morph=""
            tabindex="-1"
        ></select>
        <div class="ts-wrapper form-control form-control-solid multi plugin-remove_button plugin-virtual_scroll required invalid preloaded">
            <div class="ts-control">
                <input
                    type="text"
                    autocomplete="off"
                    size="1"
                    tabindex="0"
                    role="combobox"
                    aria-haspopup="listbox"
                    aria-expanded="false"
                    aria-controls="segment_form_filters_0_rules_0_values-ts-dropdown"
                    id="segment_form_filters_0_rules_0_values-ts-control"
                    aria-labelledby="segment_form_filters_0_rules_0_values-ts-label"
                />
            </div>
            <div class="ts-dropdown multi plugin-remove_button plugin-virtual_scroll" style="display: none; visibility: visible;">
                <div role="listbox" tabindex="-1" class="ts-dropdown-content" id="segment_form_filters_0_rules_0_values-ts-dropdown" aria-labelledby="segment_form_filters_0_rules_0_values-ts-label">
                    <div data-selectable="" data-value="5ba52e94-9c46-4c61-abd4-97f5560346eb" class="option" role="option" id="segment_form_filters_0_rules_0_values-opt-1">Accesorios de Jardinería</div>
                    <div data-selectable="" data-value="65776124-bb68-42e7-a527-966660f30c71" class="option" role="option" id="segment_form_filters_0_rules_0_values-opt-2">Piscinas y Mantenimiento</div>
                    <div data-selectable="" data-value="93430262-2e02-4fe0-8052-3091a04837db" class="option" role="option" id="segment_form_filters_0_rules_0_values-opt-8">Recambios y Accesorios Batidoras</div>
                    <div data-selectable="" data-value="0af5a430-e650-425e-ba19-5b3d085ce91c" class="option" role="option" id="segment_form_filters_0_rules_0_values-opt-10">Recambios y Accesorios Microondas</div>
                    <div class="no-more-results option">No more results</div>
                </div>
            </div>
        </div>
    </div>
</div>

And this is after changing TYPE_FAMILY to TYPE_BRAND:

<div class="col-3">
    <div>
        <label for="segment_form_filters_0_rules_0_values-ts-control" class="required" id="segment_form_filters_0_rules_0_values-ts-label">Valor</label>
        <select
            id="segment_form_filters_0_rules_0_values"
            name="segment_form[filters][0][rules][0][values][]"
            required="required"
            data-controller="symfony--ux-autocomplete--autocomplete"
            data-symfony--ux-autocomplete--autocomplete-url-value="/api/v1/search/brand"
            data-symfony--ux-autocomplete--autocomplete-max-results-value="10"
            data-symfony--ux-autocomplete--autocomplete-loading-more-text-value="Loading more results..."
            data-symfony--ux-autocomplete--autocomplete-no-results-found-text-value="No results found"
            data-symfony--ux-autocomplete--autocomplete-no-more-results-text-value="No more results"
            data-symfony--ux-autocomplete--autocomplete-preload-value="focus"
            class="form-control form-control-solid tomselected ts-hidden-accessible"
            multiple="multiple"
            data-skip-morph=""
            tabindex="-1"
        ></select>
        <div class="ts-wrapper form-control form-control-solid multi plugin-remove_button plugin-virtual_scroll required invalid preloaded">
            <div class="ts-control">
                <input
                    type="text"
                    autocomplete="off"
                    size="1"
                    tabindex="0"
                    role="combobox"
                    aria-haspopup="listbox"
                    aria-expanded="false"
                    aria-controls="segment_form_filters_0_rules_0_values-ts-dropdown"
                    id="segment_form_filters_0_rules_0_values-ts-control"
                    aria-labelledby="segment_form_filters_0_rules_0_values-ts-label"
                />
            </div>
            <div class="ts-dropdown multi plugin-remove_button plugin-virtual_scroll" style="display: none; visibility: visible;">
                <div role="listbox" tabindex="-1" class="ts-dropdown-content" id="segment_form_filters_0_rules_0_values-ts-dropdown" aria-labelledby="segment_form_filters_0_rules_0_values-ts-label">
                    <div data-selectable="" data-value="5ba52e94-9c46-4c61-abd4-97f5560346eb" class="option" role="option" id="segment_form_filters_0_rules_0_values-opt-1">Accesorios de Jardinería</div>
                    <div data-selectable="" data-value="65776124-bb68-42e7-a527-966660f30c71" class="option" role="option" id="segment_form_filters_0_rules_0_values-opt-2">Piscinas y Mantenimiento</div>
                    <div data-selectable="" data-value="93430262-2e02-4fe0-8052-3091a04837db" class="option" role="option" id="segment_form_filters_0_rules_0_values-opt-8">Recambios y Accesorios Batidoras</div>
                    <div data-selectable="" data-value="0af5a430-e650-425e-ba19-5b3d085ce91c" class="option" role="option" id="segment_form_filters_0_rules_0_values-opt-10">Recambios y Accesorios Microondas</div>
                    <div class="no-more-results option">No more results</div>
                </div>
            </div>
        </div>
    </div>
</div>

There yo can see that data-symfony--ux-autocomplete--autocomplete-url-value has been updated properly. But Tom Select has the old values and if I type in the input it use the wrong data-symfony--ux-autocomplete--autocomplete-url-value .

This is why I think it is related to the absence of the startMutationObserver call.

It's maybe related to this #1622 ? 🤔

Yes there has sadly always been some issues between Autocomplete and LiveComponents.

As you can see in the issue you link, there is an ongoing PR regarding this.

Maybe you can see if you can check / help to finish it ? :)

Are you talking about this PR #1512?

I think I don't have the knowledge to review it, but I can check if it fixes my use case and provide feedback.

I'm closing it as a duplicate of #1500