3

I'm trying to show a modal by changing the data from an external javascript file after a successful call to the Api, here's what I've tried:

<div class="modal-wrapper" x-data="modalData()">
    <div class="modal" x-show="open">
      <div class="modal-content rounded overflow-hidden shadow bg-white" id="modal-content">

      </div>
    </div>
  </div>
function modalData() {
  return {
    open: false,
    closeModal () {
      this.open = false;
    },
    openModal () {
      console.log('This gets logged just fine');
      this.open = true;
    }
  }
}

And in the successfull ajax call I'm trying to do:

modalData().openModal();
1
  • Keep in mind that every call to modalData() will create a new object. If you want it to be the same object every time then assign it to a global variable first if that global is not already set, then return that global variable instead Commented Dec 27, 2023 at 13:04

2 Answers 2

6

It's a little bit hacky solution, but you can use the _x_dataStack attribute to access the element data from the external script:

let modal = document.querySelector('.modal-wrapper')
modal._x_dataStack[0].openModal()

It uses an internal API (tested on 3.8), so it may wont work in the future. A less hacky way would be using a store and mutate the state via e.g. window.Alpine.store('open', true).

Sign up to request clarification or add additional context in comments.

Comments

3

The easiest way I have found to send data to alpine components is via custom events. You can declare a function something like this:

function ui_update( target, data ){
    var evt = new CustomEvent(target, data);
    window.dispatchEvent(evt);
}

And then listen for it in your modal:

<div x-data="{message: ''}" @set-modal-message.window="message = $event.detail.message">
    <span x-text="message"></span>
</div>

And finally in a script-tag anywhere do:

<script>

ui_update('set-modal-message', { detail: { message: 'This is my new message' } } );

</script>

Some additional notes. ui_update() is a vanilla javascript function that can be called from anywhere unlike Alpines $dispatch which can only be called from within Alpine-components (with x-data attribute).

By using .window on @set-modal-message the events can be sent from anywhere.

It would be really useful to be able to do Alpine('id of alpine component tag').something() but as far as I can tell that is not possible.

To accomplish something like that you have to declare an Alpine store from within the 'alpine:init' event on the document which you component can access and use.

document.addEventListener('alpine:init', () => {
    console.log('Alpine.js init.');
    
    Alpine.store('modal', {
        name: '', // must be defined and unique for each modal component
        is_open: false,

        show(name){
            this.name = name;
            this.is_open = true;
        },
        hide(){
            this.is_open = false;
        },
        isActive(name){
            return this.name == name && this.is_open;
        },
    });

});

You can then declare an Alpine component to use it like this:

<script>

function messageDialogComponent(){
    return {
        name: 'dialog-message',
        message: '',
    };
}

</script>
<div x-cloak
    x-data="messageDialogComponent()"
    x-show="$store.modal.isActive(name)"
    @open-modal-message.window="$store.modal.show(name); message = $event.detail.message"
    @click="$store.modal.hide()"
    class="page-overlay">
    
    <div id="dialog-message" @click.stop="">
        <div>
            <a @click.prevent="$store.modal.hide()" href="#">✖</a>
        </div>
        <div>
            <p x-text="message" class="text-center">Message</p>
        </div>
        <div>
            <button @click.stop="$store.modal.hide()">Close</button>
        </div>
    </div>
</div>

Then you can do:

Alpine.store('modal').show('name-of-your-modal');

and

Alpine.store('modal').hide();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.