Hotwire Discussion

Using select2 with turbo_frames

Hi there,

I have some forms in my app that are loaded via turbo_stream. The user select a category and, accordingly to the option, a form is generated, updating a turbo_frame. I’m trying to use select2 in the forms. Everything works fine when the page is loaded (select2 class outsite the frame), but I can’t make it work when the DOM changes. Any ideas?

My select2 file:
import $ from “jquery”;
import “select2”;

const initSelect2 = () => {
    $(function() {
        $(".js-example-basic-single").select2();
    });
};

export { initSelect2 };

I’ve tried using the code below, but with no success (only works when page is loaded)

document.addEventListener("turbo:load", function() {
    initSelect2();
});

At first, I got the select2 function to be called at each turbo stream with:
document.addEventListener(“turbo:submit-end”, function() {
initSelect2();
});

But jQuery wasn’t able to get the requested class.

So, I got things working by using select2 (and jquery) inside a stimulus controller.

if is usefull ths is a stimulus controller for slim-select and work fine in a turbo frame:

import { Controller } from "stimulus"
import SlimSelect from 'slim-select'

export default class extends Controller {
  static targets = [ 'simple', 'user', 'editor' ]

  connect() {
    if (this.hasSimpleTarget) {this.simpleTargets.forEach(element => this.simple(element))}
    if (this.hasUserTarget) {this.userTargets.forEach(element => this.user(element))}
    if (this.hasEditorTarget) {this.editorTargets.forEach(element => this.editor(element))}
  }

   simple(element) {
    new SlimSelect({ select: element })
  }

  user(element) {
    this.ajax(element, '/editor/users/list.json?')
  }

  editor(element) {
    this.ajax(element, '/admin/users/list?filter[editor]=1&')
  }

  ajax(element, url) {
    const slim = new SlimSelect({
      select: element,
      placeholder: 'Seleziona un utente',
      searchingText: 'Sto cercando...',
      searchPlaceholder:  'Digita per ricercare',
      searchText: 'Nessun risultato',
      allowDeselect: true,
      ajax: function (search, callback) {
        // Check search value. If you dont like it callback(false) or callback('Message String')
        if (search.length < 3) {
          callback('Inserire almeno 3 caratteri')
          return
        }

        // Perform your own ajax request here
        fetch(`${url}filter[text]=${search}`)
        .then(function (response) { return response.json() })
        .then(function (json) {
          let data = []
          for (let i = 0; i < json.users.length; i++) {
            data.push({text: json.users[i].username, value: json.users[i].id})
          }

          // Upon successful fetch send data to callback function.
          // Be sure to send data back in the proper format.
          // Refer to the method setData for examples of proper format.
          callback(data)
        })
        .catch(function(error) { callback(false) })
      }
    })
  }

}

you can apply the same logic to Select2 and it should work fine

1 Like

That is awesome. Thanks buddy~