AlpineJS not initializing in dynamically loaded content

40 views Asked by At

I am creating a custom searchable select widget in my django application.

When returning the template normally all is well, however Alpine is not initializing when I render this field dynamically inside a modal. I use HTMX to make the ajax request; Django responds with a template and the template gets inserted into the DOM/modal.

Things I've tried

  1. Adding my Alpine.data's data directly into the x-data. This works but there is quite a lot a would like to avoid it if possible
  2. Hacky ways to try initialize Alpine but does not work (maybe I've missed something)
  3. Hooking into HTMX beforeSwap event to try load the JS before the template (no dice)

If anyone knows how to remedy this it would be greatly appreciated

Template

<div x-data="autocomplete" x-init="init('{{ widget.value }}')" class="relative">
# template stuff...
</div>

Script

document.addEventListener('alpine:init', () => {
  Alpine.data('autocomplete', () => ({
    open: false,
    selectedValue: '',

    init(value) {
      if (!['None'].includes(value)) {
        this.selectedValue = value;
      }
    },

    # functions used inside the template...

  }));
});

1

There are 1 answers

0
Pippo On

You should explain better with examples the context and what have you tried, the request is unclear

What I can see that is wrong is that you are initializing thethe selectedValue property using the x-init directive which calls the init() method: with AlpineJs 3 the init() method is automatically called as soon as the object is created, so the result is that it runs twice, once by itself and once with your x-init.

You can make sure of this by simply adding a console.log() in init():

document.addEventListener('alpine:init', () => {

    Alpine.data('autocomplete', () => ({

        open: false,
        selectedValue: '',

        init(value) {

            console.log ('Tadah!', Date.now());

            if (!['None'].includes (value)) {
                this.selectedValue = value;
            }
        }

    }));
});

The correct way is to pass the initial parameter using the x-data as explained here:

<div x-data="autocomplete('{{ widget.value }}')" class="relative">
   .....
</div>

<script>
    document.addEventListener('alpine:init', () => {

        Alpine.data('autocomplete', (value) => ({

            open: false,
            selectedValue: '',

            init() {

                if (!['None'].includes (value)) {
                    this.selectedValue = value;
                }
            }
        }));
    });
</script>

However, you could rename the init() method for example to myInit() and call it with x-init directive, but it seems a little verbose to me