async-alpine
async-alpine copied to clipboard
Allowing to inject components inside <template>
Hi. Here's the thing
I have two ways of displaying the same component (desktop and mobile), so my initial thought was:
<template x-if="isMobile">
<div
id="componentOne"
ax-load="event"
class="contents"
x-data="componentOne()"
x-html="view"
x-ignore
></div>
<div
id="componentTwo"
ax-load="event"
class="contents"
x-data="componentTwo()"
x-html="view"
x-ignore
></div>
</template>
But this willl result in loading just the "componentOne". Then I thought "one for each component", so:
<template x-if="isMobile">
<div
id="componentOne"
ax-load="event"
class="contents"
x-data="componentOne()"
x-html="view"
x-ignore
></div>
</template>
<template x-if="isMobile">
<div
id="componentTwo"
ax-load="event"
class="contents"
x-data="componentTwo()"
x-html="view"
x-ignore
></div>
</template>
Siblings s willl result in the same error shown on issue 39 previous to 1.2.2 when trying to inject the view.
Then I use the next 6 hours to figure it out and finally got it working by implementing a new strange format, looking like this:
<div id="someContentsContainer" class="contents" x-transition.in>
<template
x-if="isMobile"
x-data="componentOne()"
id="componentOne"
ax-load="event"
x-ignore
>
<div class="contents" x-html="view"></div>
</template>
<template
x-if="isMobile"
x-data="componentTwo()"
id="componentTwo"
ax-load="event"
x-ignore
>
<div class="contents" x-html="view"></div>
</template>
</div>
As you can see, I needed to create each component inside <template>
instead of normal <div>
tag and then add a new contents containers to inject the view, which is just a raw html imported as an Alpine's instance.
Maybe this is not a bug (oddly it works) and I think it's useful to have a guide on how to apply "layouting" logic with nested components. The other option I had was to replicate the "isMobile" logic in each component which would have been sad.
Also, if you don't need a parent you can still use the classic injection
<div
id="componentOne"
ax-load="event"
class="contents"
x-data="componentOne()"
x-html="view"
x-ignore
></div>
<div
id="componentTwo"
ax-load="event"
class="contents"
x-data="componentTwo()"
x-html="view"
x-ignore
></div>
Hey @jgauna! Would you be able to create a stripped-back example of this in a repo/gist or Stackblitz? I don't understand your specific setup well enough to know what the issue is here so I'll need more to work from to be able to help.
Your first example of two divs in a single template definitely shouldn't work, Alpine requires a single root-level element within a template tag.
I'm not quite following what's happening with your second example, unless I'm mistaken Async Alpine won't evaluate those components within the x-if until Alpine places them into the DOM. That's something that I'd need to see more of your nested component tree to understand. Does that example behave as expected in version 1.2.2 if it's related to the race condition?
Hey @Accudio
- Right on the first one, I always forget that about Alpine :/
- Regarding the second example I was able to reproduce the bug here
- My strange-format-solution doesn't fully work either because it's incapable founding the children elements, so if comp1 tries to init a plugin in comp2, even after comp2 has completed loaded, comp1 won't be able to reference elements in comp2
Inspect my example and you will find an Uncaught ReferenceError: isMobile is not defined
and that's the reason of my relation to issue 39 which is realted to template nesting as my example.
Btw, I didn't know StackBlitz (thanks for that one)
Let me know if I can help
Complete log
Uncaught ReferenceError: isMobile is not defined
at [Alpine] isMobile (eval at safeAsyncFunction (alpinejs.js?v=44f523c5:477:19), <anonymous>:3:32)
at alpinejs.js?v=44f523c5:502:21
at tryCatch (alpinejs.js?v=44f523c5:416:12)
at alpinejs.js?v=44f523c5:2803:5
at reactiveEffect (alpinejs.js?v=44f523c5:1723:16)
at Object.effect2 [as effect] (alpinejs.js?v=44f523c5:1698:5)
at effect (alpinejs.js?v=44f523c5:49:33)
at wrappedEffect (alpinejs.js?v=44f523c5:65:27)
at Function.<anonymous> (alpinejs.js?v=44f523c5:2802:3)
at flushHandlers (alpinejs.js?v=44f523c5:587:46)
@Accudio Could you take a look at that example?
Hi @jgauna, sorry I wasn't able to identify what the problem you were encountering here was.
Part of that was because I have been working on a complete rewrite of the library since late April that I would expect to resolve this issue by leaning more heavily into Alpine's core plugin functionality. That rewrite launched today as version 2.0.0.
Could you please try a reduced test case using Async Alpine version 2.0.0 and see if that resolves the issue you encounter?
Thanks!
I've got some time yesterday and tested the new version; works great bro! Thanks a lot for this library; I think somehow it should be included by default in Alpine.
I've one question because I've tried with promises for awaiting module loading await Alpine.store('setup').loadComponent('my-component-1 })
loadComponent(componentId, params, callback) { return new Promise((resolve, reject) => { const detail = { detail: { id: componentId, params: params } }; window.dispatchEvent(new CustomEvent('async-alpine:load', detail)); if (callback) { callback(resolve, detail); } else { resolve(detail); } }); }
But somehow the my-component-1
data is not ready sometimes. Is there any internal method to return a promise when everything is loaded and rendered ?
@jgauna Great to hear!
If I understand correctly what you're looking for, you want to know when a component has been successfully downloaded and initialised?
I think your best bet for that is firing an event within the init
function of your component. Then you can respond in your own code accordingly. If you needed to check if the component was loaded by async alpine then the x-load
attribute would still be present.
It would be possible to add such an event to async alpine directly, but I don't think that makes sense when you can do that relatively easily within components.
I've done that pattern before on a project pre 1.0 (when nested components could be loaded independently) and it worked well.