Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Element partial templates #14284

Merged
merged 7 commits into from
Feb 4, 2024
Merged

Conversation

brandonkelly
Copy link
Member

The era of partial templating has begun!

This adds a new partialTemplatesPath config setting, which can be used to store partial templates for elements.

Partial templates can be used to render elements on the front end, based on their element type and field layout provider handle.

For example, entries of type article get the partial template _partials/entry/article.twig.

With partial templates created, templates can start calling a render() method on element queries, element collections, and even individual elements, which will render the partial template for each element, and return the result.

{{ craft.assets().volume('gallery').limit(100).render() }}
{{ entry.myMatrixField.render() }}
<ul>
  {% for related in entry.myRelationField.all() %}
    <li>{{ related.render() }}</li>
  {% endfor %}
</ul>

Copy link

linear bot commented Feb 3, 2024

@khalwat
Copy link
Contributor

khalwat commented Feb 3, 2024

Will there be any visibility in your editor as to which template is going to be rendered as a result of .render()

I'm guessing since you said it could have different templates based on the "element type and field layout provider handle" that it wouldn't even really be possible to provide that kind of information in context?

I'm just concerned that I'll be looking at some code that does .render() and I won't have any way to see in my editor what actually ends up getting rendered. I'd have to go to the CP and sift through to see the template(s) that could potentially be rendered by that element?

Also then no way to Command-click to go to the template in question in your code either.

Either way it's an optional thing, so we can pick and choose where it makes sense to use it?

Looks like an update to Template Comments to add HTML comments to see where these are coming from could be in order. I believe Drupal does this type of thing for you.

Pursuant to that, is there some way a plugin like Template Comments could hook in to add comments delineating where the rendered blocks came from?

@brandonkelly
Copy link
Member Author

I see two common use cases for it:

  • Rendering a general-purpose Matrix field where you would otherwise be loading a block/entry type template dynamically ({% include "_partials/#{entry.type.handle}" %})
  • Rendering nested entries within a CKEditor field. (← the real reason we’re doing this.)

Will there be any visibility in your editor as to which template is going to be rendered as a result of .render()

I'm guessing since you said it could have different templates based on the "element type and field layout provider handle" that it wouldn't even really be possible to provide that kind of information in context?

Depends on whether you know which entry types/volumes/etc. are being queried.

I'm just concerned that I'll be looking at some code that does .render() and I won't have any way to see in my editor what actually ends up getting rendered. I'd have to go to the CP and sift through to see the template(s) that could potentially be rendered by that element?

Yeah, but it would just be a lateral change vs. a dynamic {% include %}.

Either way it's an optional thing, so we can pick and choose where it makes sense to use it?

Right.

Looks like an update to Template Comments to add HTML comments to see where these are coming from could be in order. I believe Drupal does this type of thing for you.

Pursuant to that, is there some way a plugin like Template Comments could hook in to add comments delineating where the rendered blocks came from?

Good question. The element query itself has a rough idea from its protected fieldLayouts() method. I suppose I could make that public and rename to getFieldLayouts().

@khalwat
Copy link
Contributor

khalwat commented Feb 3, 2024

Rendering nested entries within a CKEditor field. (← the real reason we’re doing this.)

ahhhhhhhhhhh that makes sense :)

@brandonkelly brandonkelly merged commit ef01fae into 5.0 Feb 4, 2024
3 checks passed
@brandonkelly brandonkelly deleted the feature/cms-1255-element-partial-templates branch February 4, 2024 15:12
@mmikkel
Copy link
Contributor

mmikkel commented Feb 6, 2024

it would just be a lateral change vs. a dynamic {% include %}.

IMO, the difference is that with the dynamic {% include %} the developer still has complete control over where templates should be stored, how they should be structured, if more than one thing should be able to share the same template, etc.

Element partial templates is a really cool feature and I love the idea of the render() function, but TBH I'd personally prefer something like a "Partial Template" setting added to entry types (and other field layout providers), vs. Craft rigidly resolving partial templates based on nested elements' types and layout provider handles. I'm sure you considered that approach and the current implementation is probably more "streamlined", but it does feel oddly restrictive to me, especially considering how Craft is otherwise completely unassuming for things like entry URL templates.

@brandonkelly
Copy link
Member Author

@mmikkel My first pass at this was to add a “Partial Template” setting to entry types. But it felt a little cumbersome without any benefit. You’d still be limited to a single template per entry type (which, either way, can load nested templates depending on the context), and doing it that way meant any other element types that want to support partial templates would need to add their own settings. Where we landed, now every element type gets it for free – so long as they define a refHandle and field layouts are populated with a provider object (e.g. the entry type).

@mmikkel
Copy link
Contributor

mmikkel commented Feb 6, 2024

But it felt a little cumbersome without any benefit.

The benefit would be that developers would be free to structure their element partial templates however they see fit, which IMO is a pretty big one. To make it less cumbersome for people who are fine with defaults, Craft could even auto-fill the "Partial Template" setting for new entry types – just like how it autofills the "Template" setting when creating new sections.

Where we landed, now every element type gets it for free

Fair point, although I'd argue that adding support for partial templates would probably be a relatively minimal effort compared to the overall amount of work that goes into creating a custom element type.

Anyway, this isn't the biggest thing in the world – just wanted to give my 2 cents 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants