I'm building a small custom web components engine for a personal project and would love to use alpinejs for reactivity. The problem I have though is that I don't know how to set the data from javascript. As far as I understand it, AlpineJS only allows setting the data globally by attribute. Is there any way I can bind the data dynamically from javascript without polluting global scope?
Not sure if it fits your user case but you can use an empty x-data and set up your scope in x-init.
For example
<div x-data x-init="app = {foo:'bar'}" >
<span x-text="app.foo"></span>
</div>
Obviously this example is a bit silly, I assume you want to use an ajax call returning a json instead of hardcoding {foo:'bar'}.
This is quite useful but not exactly what I want.
I am creating elements dynamically with appendChild (cloning a template into a shadow dom). Every of these elements should have their own data, supplied not in the html but from outside. this is what I would like to be able to do:
var data = {}
htmlElement.xData = data;
This would bind the data not to a global object but to the supplied JS Object
Alpine doesn't work too well with components added after the DomReady event.
From what you describe, if your element are not too complex, you can maybe use the x-for directive.
https://codepen.io/SimoTod/pen/MWwLYjw
If you want to stick with your original plan, I think you need to manually instantiate your items for now.
Something like https://codepen.io/SimoTod/pen/KKpJwBd should work but it's vanilla JS more than Alpine.
I'd probably go with what Simone suggested too. There's probably some stuff that could be worked on for the mutation observation. I'll look into that tomorrow.
Hey @Chrisstar56, I was also working on some personal project which had a similar use case. However, I found a workaround to deal with the dynamic data population by mounting a pseudo-element. Here is a bare bones example -
<body x-data="{state: {}}" @mounted="state[$event.detail.name] = $event.detail.data">
<div x-show="state.primary">primary</div>
<div x-show="state.secondary">secondary</div>
</body>
<script>
const mount = (name, data) => {
const e = new CustomEvent('mounted', { detail: { name, data } });
document.body.dispatchEvent(e);
}
setTimeout(mount, 2000, 'primary', true);
setTimeout(mount, 5000, 'secondary', true);
</script>
Okay thank you all, but I've decided to stick with Vue for this project (probably better because it will grow quite a bit in the future). I still think Alpine has great potential and think that this is something that it will quite benefit from (so I would suggest leaving this open as a suggestion).
Maybe a solution would be to make Alpine not initialise by itself, but on an element (like many other front-end librarys). Something like Alpine.init(element, data) maybe?
Okay thank you all, but I've decided to stick with Vue for this project (probably better because it will grow quite a bit in the future). I still think Alpine has great potential and think that this is something that it will quite benefit from (so I would suggest leaving this open as a suggestion).
Maybe a solution would be to make Alpine not initialise by itself, but on an element (like many other front-end librarys). Something like Alpine.init(element, data) maybe?
It's a possibility but it opposes the Alpine philosophy at the moment. Alpine is designed to work with the markup that you already have, without needing any of your own JavaScript (90% of the time). There are plans for an Alpine.component() method which might solve your use case.
Regarding the previous comment I made, I've opened up a separate issue (#359) that discusses a similar approach mentioned by @Chrisstar56 as an alternative to using Alpine's automatic initialisation. I'll close this issue now and move discussion on that topic over to the other issue. Thanks! :)
Most helpful comment
Okay thank you all, but I've decided to stick with Vue for this project (probably better because it will grow quite a bit in the future). I still think Alpine has great potential and think that this is something that it will quite benefit from (so I would suggest leaving this open as a suggestion).
Maybe a solution would be to make Alpine not initialise by itself, but on an element (like many other front-end librarys). Something like Alpine.init(element, data) maybe?