I have a Alpine JS template that i want to reference x-data
from an array. I currently have that declared at the bottom of my HTML file in a <script>
tag, and i would like to move it in an external file. If I create the file and link it with <script src='path/to/file.js'>
my component stops working.
Example that works:
<div x-data="{items}">
<template x-for="(item, index) in items" :key="index">
<a :href="item.link" x-text="item.text"></a>
</template>
</div>
<script>
const items = [
{ link: 'foo.com', text: 'foo' },
{ link: 'bar.com', text: 'bar' },
]
</script>
Example that doesn't work, where external.js
has the same variable assignment
<div x-data="{items}">
<template x-for="(item, index) in items" :key="index">
<a :href="item.link" x-text="item.text"></a>
</template>
</div>
<script src="external.js"></script>
And the contents of external.js file:
import 'alpinejs'
window.onload = () => {
console.log('loaded') // This logs in the console
var items = [
{ link: 'foo.com', text: 'foo' },
{ link: 'bar.com', text: 'bar' },
]
}
I'm not sure what i'm doing wrong, help :(
Ok, so your problem is you're creating your items
variable in a callback that listens to the window.onload
event. AlpineJs will have already loaded and tried to parse the DOM including your variable before the event fires and your variable is defined.
We can see the load order by adding a console.log
to the x-init
attribute on an Alpine component:
<div x-data="{}" x-init="console.log('init')"></div>
<script>
window.onload = () => {
console.log('loaded')
}
</script>
With this in place here's what we get in dev tools (working example https://jsfiddle.net/hfm7p2a6/):
Fixing this depends a little on what you need to be in items
. If there's no dynamic content being loaded (i.e. you're not needing to do an ajax call to get the content of your array) then simply forget the onload
and place the variable in the top level of the script file (working example https://jsfiddle.net/nms7v4xh/):
// external.js
var items = ['your', 'items', 'array'];
// No window.onload needed
If you do need there to be dynamic content in there, you'll need to inject it into the AlpineJs instance. The best way to do this here is probably via a custom event (working example https://jsfiddle.net/6dLwqn4g/1/):
<div id="app" x-data="{items: null}" @my-event.window="items = $event.detail.items"></div>
<script>
// Create and dispatch the event
let event = new CustomEvent('my-event', {
detail: {
items: ['your', 'dynamic', 'items', 'content']
}
});
window.dispatchEvent(event);
</script>
Here we're using AlpineJs to listen for an event on the window using the @my-event.window
attribute (you could also use x-on:my-event.window
). The items data is then available to us via the $event.detail
property.
Hope that's helpful! Some extra reading below if you need.
https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With