Replies: 5 comments 14 replies
-
Thanks for the proposal!! Eliminating action verbsI considered doing this initially, as I agree that it keeps you closer to the “metal”. What swung me in the direction I took was:
Namespacing the storeI really like the idea of injecting a Automatic updating of the storeIf I inject a The nameNot sure about this one. I’ll have to let it sit. |
Beta Was this translation helpful? Give feedback.
-
I agree with @khalwat’s suggestion that instead of using From @khalwat’s comment:
I do like that Spark helps with the CSRF stuff, so having to get that yourself makes Spark less elegant, but maybe another Twig method might help. Something like, Regarding the name and the API in general, I think it does make sense to either go all in on Datastar and make this plugin a way to do everything Datastar can do with some helpers to make the requests between the front end and back end easier. Another option might be to abstract all of this stuff and make an original reactive framework and just use Datastar under the hood to power it. I think the latter would be a ton of work and I'm not sure that was the original intent for Spark. |
Beta Was this translation helpful? Give feedback.
-
I just tagged 0.0.5 with some syntax changes. Eliminating action verbsI took your advice and it is now required to output <button data-on-click="$$get('{{ sparkUrl('_spark/main.twig') }}')"> I addressed the CSRF issue using a third argument. <button data-on-click="$$post('{{ sparkUrl('_spark/main.twig', [], true) }}')"> Namespacing and updating the storeDatastar's store values can now be accessed and modified within templates rendered by Spark using the Username: {{ store.username }}
{# Modifies the value of `username` in the store. #}
{% do store.set('username', 'bobby') %}
{# Modifies multiple values in the store. #}
{% do store.setValues({ username: 'bobby', saved: true }) %} The Dual APIsI considered and even built out the tags I also want to abstract away Datastar’s event types and implementation. I want there to be no need for the user to understand what In the end, I opted to create separation of concerns by using |
Beta Was this translation helpful? Give feedback.
-
I just tagged 0.0.6 with some syntax changes. The third argument for <button data-on-click="$$post('{{ sparkUrl('_spark/main.twig', [], 'post') }}')"> This is because I’ve reintroduced a short-hand version <button data-on-click="{{ spark('_spark/main.twig', [], 'post') }}"> I’ve also reintroduced the |
Beta Was this translation helpful? Give feedback.
-
Tagged 0.0.7 with support for the |
Beta Was this translation helpful? Give feedback.
-
I have a proposal to change the Spark API, predicated on the idea that the real purpose of Spark is:
I think rather than creating a new "sort of similar but different" API on top of Spark isn't the best route, because:
I realize the desire to decouple Spark from Datastar, but the reality is that unless you find another library that uses the
data-
attributes in exactly the way Datastar does, your code is going to end up being tightly coupled to Datastar anyway.So instead, embrace it.
My proposal largely centers around embracing the existing APIs in Datastar, Twig, and Craft and using them rather than creating your own "new and similar but a little different" API on top.
It also has the thread running through it that we shouldn't obfuscate what's actually going on, because that generally leads to a steeper learning curve in the long run.
Here's what I propose changing:
Eliminating action verbs
data-on-click="{{ spark.get('some/template.twig') }}"
->data-on-click="$$get({{ spark.renderTemplate('some/template.twig') }})"
data-on-click="{{ spark.put('some/template.twig') }}"
->data-on-click="$$put({{ spark.renderTemplate('some/template.twig') }})"
data-on-click="{{ spark.post('some/template.twig') }}"
->data-on-click="$$post({{ spark.renderTemplate('some/template.twig') }})"
data-on-click="{{ spark.patch('some/template.twig') }}"
->data-on-click="$$patch({{ spark.renderTemplate('some/template.twig') }})"
data-on-click="{{ spark.delete('some/template.twig') }}"
->data-on-click="$$delete({{ spark.renderTemplate('some/template.twig') }})"
In other words, have Spark just render the appropriate URL that will render a given Twig template, rather than output the Datastore JavaScript code surrounding it, similar to how Craft's baked in
{{ actionUrl() }}
works.This makes it more transparent in terms of what is actually going on (we're rendering a Twig template), and allows you to use existing Datastor sample code by just changing the URL.
Alternatively, you could use the verb
.renderFragment()
if you wanted to distinguish the templates from regular old Twig templates.This becomes more important when you realize that everything that is in the
data-on-*
event handlers is just JavaScript code, and can be (and often will be) more elaborate than just rendering an endpoint, as shown in this example: https://datastar.fly.dev/examples/dialogs_browserUsed with Spark and my proposed changes, this would just become:
I think this makes it clearer what is actually going on, and lets you leverage existing Datastar documentation and example code easier.
Namespacing the store
The other thing I'd like to see is that since you reference items in the Datastore with a
$
in front of them in JavaScript on the frontend (e.g., accessing thesearch
item in the store is done via$search
), I'd ideally like to see the store variables that are injected into the Twig template be prefixed with$
too, to distinguish them from regular Twig variables.Sadly, there doesn't appear to be a way to do this, because
$search
, for example, is an invalid variable name in Twig.What you could do, though, is put all of the variables in one object, rather than polluting the template context with a potentially huge number of variables that could collide with variables you use in your templates with transparency.
So instead of the
search
variable being set in the Twig context due to the fact thatsearch
is in your Datastore context, you'd reference it viastore.search
or some other variable name of your choosing.I think this makes it more explicit in terms of what's happening in your templates (you can plainly see that the value is coming from the Datastar
store
rather than it just being a magically set variable) and it prevents namespace collisions that will likely happen inadvertently for templates of any complexity. This is especially important if different people are working on different aspects of the code.Automatic updating of the store
The other thing that namespacing the store would let you do pretty cleanly would be you could eliminate entirely the
{% do spark.setStore({ search: 'foo' }) %}
.Instead, treat the
store.
as a special reactive object store that automatically does a diff on any changes in it, and sends them back to the Datastar frontend.Granted, Twig makes this slightly annoying, in that you have to use the
| merge
filter to update the store object, but it's a pattern people are used to and familiar with working with in Twig.The name
I'd also just drop the pretense of Spark being its own new, differently branded thing, and call it Craft Datastar
The Dual APIs
Spark has two APIs:
Currently, both APIs are available under the
spark.
namespace, but there's a problem: the Frontend APIs can only be used in the regular Twig templates, and the Backend APIs can only be used in the fragment templates.Also currently, it's a little strange in that what's actually happening is obfuscated. Fragment templates have an implicit Fragment SSE that sends along whatever the Twig template renders. But also there are methods like
spark.delete()
andspark.redirect()
and such that send additional explicit events.I think it is confusing that parts of the
spark.
API work in regular Twig templates but don't work in fragment templates, and other parts of thespark.
API work in fragment templates, but don't work in regular Twig templates.My general advice when writing an API is to simplify but don't obfuscate. People more easily get their head around APIs that are consistent in terms of scope and functionality.
So much as I've advocated for Spark to spend less work making it's own API with the various
spark.get
/spark.put
verbs, and instead just have aspark.renderTemplate
method, I'm also going to suggest making the fragment templates declaratively show what's going on, rather than hide it.So with that in mind, instead of a bifurcated API where parts works in regular Twig templates, and part works in fragment templates, here's what I'm proposing:
spark.
API is available only in regular Twig templatesfragment
SSE, and instead do it explicitlySo what I propose is explicitly showing people that they aren't really rendering a Twig template, they are sending events back to Datastar on the frontend:
And we do the same thing for the other events:
(you can add whatever modifiers you need to for the
send <event>
, e.g.send console **log**
as it makes sense)Most of the time people will just be doing
{% send fragment %}
which isn't that far off what you're doing already with your{% fragment %}
tags currently, it's just more explicit. And then for the more rare times people are sending back the other events, you have a consistent way to do it.Now we have a consistent way to represent all of these things, and we're doing it in a way that makes it explicit what is actually happening: we're sending events back to Datastar on the frontend.
And we also now have separated the regular
spark.
API that's used in regular Twig templates from the API that is used in fragment Twig templates clearly and cleanly.This way is also more easily extensible when you need to add more event types that Spark can send back as Datastar evolves.
To be nice, trim the whitespace from the beginning and end of everything in the
{% send %}
blocks.If you don't like the
send
verb, you could also doevent
orsendEvent
instead, but I thinksend
is pithy and conveys what is actually going on better.Beta Was this translation helpful? Give feedback.
All reactions