Open JuroOravec opened 2 months ago
Not knowing AlpineJS, I'm trying to wrap my head around this. Are you saying that we should add support for AlpineJS to slot tags? I was hoping that we could avoid having any code that is specific to a certain library in django_components, and that this was something that anyone can add in a separate library? Are you thinking differently?
I'm thinking the same, ideally it would be a plugin. But right now it's hard to imagine how plugins should look like, so I was thinking of first implementing this, and then reverse-engineering API for plugins. But we can also do it in a single step.
For the Alpine slots support, I imagine the plugin would:
alpine="{}"
keyword, which the plugin would detect, and remove from inputs.Once a tag was been processed into a Node, the plugin would then need to store the plugin-specific data somewhere. So each Node would have a meta
or extra
attribute, where plugin authors could store the extra data that could then later be accessed in render phase.
So e.g. given {% slot alpine="{ abc: 123 }" %}
, the plugin would extract and set SlotNode.meta["alpine_slots__data"] = "{ abc: 123 }"
alpine_slots__
being a unique prefix for the plugin.
Not sure if this warrants the complexity of maintaining a whole plugin system, but you're doing the work here :)
Just wanted to chime in that I'm running into this issue as well and would make use of a plugin that addresses it
@jdare-compass @zachbellay Tell us more. In your own words: What is the problem, and how should we solve it in your opinion?
Background
When I have a slot with
isolated
context, I expect that the variables accessible inside the slot are the same as outside of the component. In other words, whatever variables the component defines, it does NOT leak into the slot, unless explicitly set:For the UI component library, I'm using AlpineJS. To emulate slot Vue behaviour, I need this behavior of isolated context to be true also for AlpineJS variables when using
context_behavior="isolated"
:Implementation
I was able to achieve this using x-teleport like so:
{% slot %}
tag, I wrap it in two HTML elements,<template>
and<div>
Notes:
<template>
is required by AlpineJS, and that's where we define the teleport<div>
inside the<template>
is used for 2 reasons:<div>
, we allow the actual slot content to be anything.{% slot data="var" %}
Accessing slot data
By adding
x-data
to the<div>
, I am able to expose the Alpine data to the slot the same way I can expose Django data to the slot using{% slot data="var" %}
:So, from within the slot, the data can then be accessed as
$slot
:API
Ideally, this would be marked on the
{% slot %}
tag. Plus we need to allow define what Alpine data should be exposed to the slot.So it could look like this:
If the slot HAS an
alpine
keyword, we render the<template x-teleport="#abc">
at the very start or an end of the django template. The{% slot %}
would be inside the<template>
. And, in place of the original slot position, we insert<div id="abc"></div>
.Other considerations
The generated IDs could look like "slot-{component_id}-{slot_name}", e.g.
slot-a0b1c3-content
Because we use the
<div>
inside the x-teleport, and another to specify the target ID, it means that slots using thealpine
keyword would have 2 extra<div>
s wrapping the actual content.So if I had
In the slot's position, we'd actually render:
This could break some styling setups, as users wouldn't be able to modify the divs.
So instead we could add CSS classes to the slots:
Then, together with scoped CSS, people would be able to style these as: