Search code examples
javascriptjqueryalpine.js

Trigger alpine function from vanilla javascript


I created an AlpineJs Toaster and I want to trigger it from javascript code to call it from anywhere.

I tried to update the store but it's not working. What I am missing ?

$("#triggerBtn").on("click", function(){  //that just an example with jquery
    Alpine.store('toast',{type:"danger",message:"hello",visible:true});
})

document.addEventListener("alpine:init",()=>{
    Alpine.store("toast",{type:null,message:null,alerts:[]});
    Alpine.data('toast', () => ({
        type:null,
        message:null,
        alerts:[],
        init(){
            var $store = Alpine.store("toast");
            this.type = $store.type;
            this.message = $store.message;
            this.alerts = $store.alerts;

            var icon = "fa fa-info-circle text-info-600";

            this.alerts.push(
                    {
                        icon:icon,
                        message:this.message,
                        visible:true
                    }
            Alpine.store('toast').alerts = this.alerts;
        },
        close(){
            this.show=false
        }
    }))
})
<div x-data="toast">
    <template x-for="alert in $store.toast.alerts">
        <div x-show="alert.visible" id="liveToastAlpine"
            class="mb-3 z-40 drop-shadow-xl flex items-center w-full max-w-xs p-4 text-gray-500 bg-white rounded-lg"
            role="alert">
            <div
                class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-info-500 bg-info-100 rounded-lg dark:bg-info-700 dark:text-info-200">
                <i x-bind:class="alert.icon" class="fas"></i>
            </div>
            <div class="ms-3 text-sm font-normal" x-text="alert.message"></div>
            <button type="button" class="ms-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg"
                data-dismiss-target="#liveToastTemplate" aria-label="Close" @click="alert.visible=false">
                <span class="sr-only">Close</span>
                <i class="fas fa-times"></i>
            </button>
        </div>
    </template>
</div>

Solution

  • It is not necessary to rely on the toast object for management as the for loop can rely directly on the store.
    Note that the magic property $store is already defined in Alpine, so variables with the same name should not be declared.
    Since the store allows you to add methods to interact with the data, a possible solution is to move the logic there:

    <div>
    
        <div class="m-2">
            <button id ="triggerBtn"
                    class="bg-blue-500 hover:bg-blue-700 text-white py-2 px-4 rounded"
            >
                New alert
            </button>
        </div>
    
        <div x-data>
    
            <template x-for="(alert, index) in $store.toast.alerts" :key="alert.key">
    
                <div class="mb-3 z-40 drop-shadow-xl flex items-center justify-around w-full max-w-xs p-4 rounded-lg"
                     :class="{'bg-green-100 text-green-800': alert.type == 'info',
                              'bg-yellow-100 text-yellow-800': alert.type == 'warn',
                              'bg-red-100 text-red-800': alert.type == 'error'
                            }"
                     role="alert"
                >
                    <div class="inline-flex rounded-lg">
                        <i x-bind:class="alert.icon" class="fas"></i>
                    </div>
    
                    <div class="ms-3 text-sm font-normal"
                         x-text="alert.message"
                    >
                    </div>
    
                    <button type="button"
                            class="p-1 rounded-lg"
                            :class="{'text-green-900 hover:text-green-600': alert.type == 'info',
                                     'text-yellow-900 hover:text-yellow-600': alert.type == 'warn',
                                     'text-red-900 hover:text-red-600': alert.type == 'error'
                            }"
                            aria-label="Close"
                            @click="$store.toast.remove(index)"
                    >
                        <span class="sr-only">
                            Close
                        </span>
    
                        <i class="fas fa-times"></i>
    
                    </button>
    
                </div>
    
            </template>
    
        </div>
    
    </div>
    
    
    <script>
    
        // this is for testing
        window.alertTypes = [
            {message: "Info message", type: "info"},
            {message: "Warning message", type: "warn"},
            {message: "Error message", type: "error"},
        ];
    
        document.getElementById("triggerBtn").addEventListener("click", function () {
            const {message, type} = window.alertTypes[Math.floor(Math.random() * 3)]; // the message is set randomly for testing
            Alpine.store("toast").add(message, type);
        });
    
        document.addEventListener("alpine:init", () => {
    
            Alpine.store ("toast", {
    
                alerts: [],
    
                add: function (message = "", type = "info") {
                    this.alerts.push ({type: type, message: message, key: Date.now()})
                },
    
                remove: function (index) {
                    this.alerts.splice(index, 1);
                }
            });
        });
    
    </script>
    

    Instead of hiding the messages, in my solution I decided to remove the related row from the array, so I removed the visible property but I added a key property (valued with a unique value derived from the date) to allow the x-for loop to understand what was added or removed

    I've also added a simple management for the colors depending on the alert type

    For testing purpose I inserted a global list of values ​​and a button that uses javascript to take a random value from this and add it to the alerts shown

    In short, triggering the display of a message is implemented by adding a line, via javascript, to the list stored in the Alpine store

    Here the documentation for the Alpine store