-
Notifications
You must be signed in to change notification settings - Fork 21
Proposal: turbo_frame
tag.
#5
Comments
Hey @blopker, thanks so much for putting this proposal together! Overall, I think this makes a lot of sense. The one part that I wanted to ask about was the specific API around lazy-loaded frames. As far as I know, there's nothing stopping you from naming a url route I imagine it would be cleaner to either separate the functionality into a separate tag, or have the API for the Hope that makes sense! Thanks again for writing this up, and happy holidays! |
Thanks for the feedback! You're spot on about the laziness being ambiguous. I was trying to be too clever... I like both your suggestions. I'm leaning towards using |
This proposal looks great to me as it stands right now! Tagging @C4ptainCrunch in case they'd like to take a look. @blopker, would you like to work on the implementation for this? If not, I can try and take a crack at it sometime in the next few days, just let me know! |
You would probably need to have the ability to pass in variables as with an |
Yeah, I got it all working with the example chat app, both static and lazy frames, but you've read my mind. We don't get much from having it. One thing we can do is auto-generate the frame ID like the Rails version. It's one less thing to think about in the simple case I guess. The other advantage is we get a facade to defend our users from breaking changes in the underlying library, or in case we do add extra features later. Such as some kind of error checking. Users will get it for free. Implementation as of now (no ID generation): # turbo/templatetags/turbo.py
from django import template
from django.template import Template
from django.urls import reverse
register = template.Library()
turbo_frame_tpl = Template(
"""
<turbo-frame {{ turbo_args|safe }}>
{% include turbo_template %}
</turbo-frame>
""")
@register.inclusion_tag(turbo_frame_tpl, takes_context=True)
def turbo_frame(context, turbo_template=Template(''), turbo_id=None, turbo_target=None, turbo_src=None, **kwargs):
turbo_args = f'id="{turbo_id}"'
if turbo_src is not None:
if '/' not in turbo_src:
turbo_src = reverse(turbo_src, kwargs=kwargs)
turbo_args += f' src="{turbo_src}"'
if turbo_target is not None:
turbo_args += f' target="{turbo_target}"'
params = {'turbo_template': turbo_template, 'turbo_args': turbo_args}
params.update(context.flatten())
return params |
OK, but this makes anything beyond the very simple use case difficult: for example, let's say we have a row of items, and each item has it's own frame. Here's the non-template tag version:
This is a bit contrived, but a couple things: first it's easy to build a frame ID as it's just interpolated in-template markup. Second I can use an include, which means I can pass in scoped variables using "with". While these are doable in a template tag, it adds more complexity than it takes away. Doing the same thing with a tag:
I'd argue the first example is easier to write and maintain, plus I can keep that "css_class" argument scoped to the include template. While there may be some changes to the markup, I'd argue this is a good argument for not adding a template tag, as we don't know how much the underlying semantics and behavior will change as well. Hotwire is very beta and as more people start using it and making PRs as they run into limitations it's likely to change quite a bit (right now it's really only used in serious production by one team on one project), so we should avoid too much premature abstraction while it all shakes out. |
One of the reasons I think some kind of abstraction over the Turbo elements is useful is parity with Passing context down to the child template will definitely be necessary, and might be able to be accomplished by subclassing the I tend to agree though with your comment in #4 that most of the benefits that |
Hey, regarding lazy loading... I wanted to share my work so far here. An Example would look like that in code: {% lazy 'apps.core.partial_views._machine_card' poll_status.machine_id %}
{% include 'core/partials/_machine_card_loading.html' with name=poll_status.name machine_id=poll_status.machine_id %}
{% endlazy %} So the inner part would be rendered initially and the part given in the What do you think about this approach to lazy loading @blopker and @davish ? |
Hey team, I'm pretty excited about the prospect of writing less JavaScript and so, am pretty excited about this project.
After wrapping my head around the Rails version of the frames tag (https://github.com/hotwired/turbo-rails/blob/main/app/helpers/turbo/frames_helper.rb), I've come up with what I think is the equivalent in Django land. Let me know what you think!
Simplest version
This renders a Django template inline, passing the parent template context to the child template like an
include
tag would. The tag would wrap the child context with<turbo-frame>
, like the Rails version does. This simple version would auto-generate a frame ID based on this template name, in this casemyapp-mytemplate
. It could also add that ID to the parent context (turbo_id
) as it passes the context to the child template.Tag:
Template:
Output:
Tag with arguments
This demonstrates overriding default behavior to the previous tag. Note all arguments are prefixed with
turbo
to avoid collisions in the lazy tags below.turbo_target
: Custom target. Optional.turbo_id
: Custom frame ID. Works for both lazy and non-lazy frames. Optional.Tag:
Template:
Output:
Lazy frame
This demonstrates using the tag with lazy loading. The first argument is still a template path, but this will be replaced once the remote content loads. In a lazy frame the template argument is also optional.
turbo_src
is either a URL or a named URL and makes the frame "lazy". The idea is to be similar to how theurl
tag works. Unknown arguments are passed to URL resolver, just like with the builtinurl
tag. Arguments prefixed withturbo
are used to configure the frame. This adds a new option,turbo_loader
, that only works with lazy frames. The previous options work here too. For lazy frames the auto generated ID is based onturbo_src
and the arguments passed.turbo_src
: Named URL or URL. Turns the frame into a lazy frame. Optional.Tag:
Template:
Output:
The text was updated successfully, but these errors were encountered: