Symfony UX - TomSelect Autocomplete custom js controller problem

369 views Asked by At

I'm struggling with configuration of Symfony UX Autocomplete component extension - My goal is to modify TomSelelct Instance - the way how its rendered and add options like https://tom-select.js.org/plugins/checkbox-options/

I have a working example of autocomplete defined like this:

EntityAutocompleteField.php

#[AsEntityAutocompleteField]
class EntityAutocompleteField extends AbstractType
{
    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'class' => Entity::class,
            'searchable_fields' => ['name'],
            'label' => 'entity',
            'choice_label' => 'name',
            'attr' => [
                'data-controller' => 'custom-autocomplete', //this should point me to custom controller - right?
            ],
            'multiple' => true,
        ]);
    }

    public function getParent(): string
    {
        // In 2.13 it will be replaced by BaseEntityAutocompleteType
        return ParentEntityAutocompleteType::class;
    }
}

In ./assets/controllers.json

{
    "controllers": {
        "@symfony/ux-autocomplete": {
            "autocomplete": {
                "enabled": true,
                "fetch": "eager",
                "autoimport": {
                    "tom-select/dist/css/tom-select.default.css": true,
                    "tom-select/dist/css/tom-select.bootstrap5.css": false
                }
            }
        }
    },
    "entrypoints": []
}

In ./assets/controllers/custom-autocomplete_controller.js:

import { Controller } from '@hotwired/stimulus';

/* stimulusFetch: 'lazy'*/ //I am not sure if this shoul dbe lazy - it does not work without this line anyway 
export default class extends Controller {

  initialize() {
//THIS IS NOT WORKING
    console.debug("Init");
    this._onPreConnect = this._onPreConnect.bind(this);
    this._onConnect = this._onConnect.bind(this);
  }

  connect() {
    console.debug("Connect");

    this.element.addEventListener('autocomplete:pre-connect', this._onPreConnect);
    this.element.addEventListener('autocomplete:connect', this._onConnect);
  }

  disconnect() {
    console.debug("Disconnect");

    // You should always remove listeners when the controller is disconnected to avoid side-effects
    this.element.removeEventListener('autocomplete:connect', this._onConnect);
    this.element.removeEventListener('autocomplete:pre-connect', this._onPreConnect);
  }

  _onPreConnect(event) {
    // TomSelect has not been initialized - options can be changed
    console.log(event.detail.options); // Options that will be used to initialize TomSelect
    event.detail.options.onChange = (value) => {
      // ...
    };
  }

  _onConnect(event) {
    console.log(event.detail.tomSelect); // TomSelect instance
    console.log(event.detail.options); // Options used to initialize TomSelect
  }
}

In my ./webpack.config.js I have only enabled stimulus bridge, and added main app.js:

Encore
 ...
    .addEntry('app', './assets/js/app.js')
    .enableStimulusBridge('./assets/controllers.json')

In my ./assets/js/app.js

import { startStimulusApp } from '@symfony/stimulus-bridge';

export const app = startStimulusApp(require.context(
  '@symfony/stimulus-bridge/lazy-controller-loader!./../controllers/',
  true,
  /\.[jt]sx?$/
));

The logic of autocomplete is working fine but I cant find a way to modify TomSelect instance - seems like the custom-autocomplete controllers is not loaded/found as there are no logs in the console.

I can see the controller in ./public/build directory after I made

bin/console assets:install
yarn install
yarn build
bin/console cache:clear
yarn watch 

In my composer json there are new lines;

    "@hotwired/stimulus": "^3.0.0",
    "@symfony/stimulus-bridge": "^3.2.0",
    "@symfony/stimulus-bundle": "file:vendor/symfony/stimulus-bundle/assets",
    "@symfony/ux-autocomplete": "file:vendor/symfony/ux-autocomplete/assets",
    "tom-select": "^2.2.2",

In yarn.lock :

"@symfony/stimulus-bridge@^3.2.0":
  version "3.2.2"
  resolved "https://registry.yarnpkg.com/@symfony/stimulus-bridge/-/stimulus-bridge-3.2.2.tgz#afc1918f82d78cb2b6e299285c54094aa7f53696"
  integrity sha512-kIaUEGPXW7g14zsHkIvQWw8cmfCdXSqsEQx18fuHPBb+R0h8nYPyY+e9uVtTuHlE2wHwAjrJoc6YBBK4a7CpKA==
  dependencies:
    "@hotwired/stimulus-webpack-helpers" "^1.0.1"
    "@types/webpack-env" "^1.16.4"
    acorn "^8.0.5"
    loader-utils "^2.0.0"
    schema-utils "^3.0.0"

"@symfony/stimulus-bundle@file:vendor/symfony/stimulus-bundle/assets":
  version "1.0.0"

"@symfony/ux-autocomplete@file:vendor/symfony/ux-autocomplete/assets":
  version "1.0.0"

"@hotwired/stimulus-webpack-helpers@^1.0.1":
  version "1.0.1"
  resolved "https://registry.yarnpkg.com/@hotwired/stimulus-webpack-helpers/-/stimulus-webpack-helpers-1.0.1.tgz#4cd74487adeca576c9865ac2b9fe5cb20cef16dd"
  integrity sha512-wa/zupVG0eWxRYJjC1IiPBdt3Lruv0RqGN+/DTMmUWUyMAEB27KXmVY6a8YpUVTM7QwVuaLNGW4EqDgrS2upXQ==

"@hotwired/stimulus@^3.0.0":
  version "3.2.2"
  resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.2.2.tgz#071aab59c600fed95b97939e605ff261a4251608"
  integrity sha512-eGeIqNOQpXoPAIP7tC1+1Yc1yl1xnwYqg+3mzqxyrbE5pg5YFBZcA6YoTiByJB6DKAEsiWtl6tjTJS4IYtbB7A==


tom-select@^2.2.2:
  version "2.3.1"
  resolved "https://registry.yarnpkg.com/tom-select/-/tom-select-2.3.1.tgz#df338d9082874cd0bceb3bee87ed0184447c47f1"
  integrity sha512-QS4vnOcB6StNGqX4sGboGXL2fkhBF2gIBB+8Hwv30FZXYPn0CyYO8kkdATRvwfCTThxiR4WcXwKJZ3cOmtI9eg==
  dependencies:
    "@orchidjs/sifter" "^1.0.3"
    "@orchidjs/unicode-variants" "^1.0.4"

2

There are 2 answers

0
Tomasz Buczeń On

I have removed node-modules directory along with vendor - run composer install again.

I made sure that in composer.lock there were only 2 new items:

"name": "symfony/ux-autocomplete",
...
"name": "symfony/stimulus-bundle",

After this hard reset I could see the proper logs in the console coming from custom-autocomplete_controller.js

Then in _onPreConnect function I could modify TomSelect options

_onPreConnect(event) {
    // TomSelect has not been initialized - options can be changed
    let options = event.detail.options;

    options.render = {
      option: function(data, escape) {
        return '<div>' + escape(data.text) + '</div>';
      },
    };

    options.plugins.checkbox_options = {};
    options.plugins.clear_button = {};
    delete options.plugins.remove_button;
}

I still dont know if the styling I made was done in proper way but I have just added separate stylesheet for tomSelect that I later import in main style.scss

@import 'components/tomSelect';

Anyway - that works OK.

0
Thierry On

According to documentation you should remove your/* stimulusFetch: 'lazy' */

The extending controller should be loaded eagerly (remove /* stimulusFetch: 'lazy' */), so it can listen to events dispatched by the original controller.