I’ve been putting it off but the need finally arose for rendering a card from another cards $foreach
.
You can already render cards-within-cards in static form using the blocks:
attribute to specify the #id
of cards you want to render from within the content:
template. A common example is rendering a card as a sidebar:
@card card_1 {
blocks: [#sidebar]
content: ```
<div class="column">${sidebar}</div>
<div class="column">Other stuff</div>
```
}
When #card_1
gets rendered the card #sidebar
gets rendered first and the rendered content is automatically bound under the same name. So the sidebar
binding contains the rendered content of the #sidebar
card and can be interpolated using the expression ${sidebar}
.
However this doesn’t help if you (a) don’t know which card you want to render, or (b) want to render it more than once as you do in a $foreach
.
I just about got this working by implementing a render_card
filter but it was pretty… unergonomic:
@card c_char_list
bindings: [
characters: *game.characters
]
content: ```
$foreach(c:characters) {%
${"c_char_desc" | render_card: c, $block, …}
%}
```
}
First this is rather opaque looking, it’s not at all easy to see what is happening here.
Second, passing the card id as a string expression to be filtered is just janky.
Third, having to pass the $block
binding to the filter. You have to because the renderer can’t do its thing without it but, ugh.
Lastly, how to specify parameters? Actually this is what broke me, filters aren’t meant for this level of complexity and filter parameters don’t parse object syntax.
At this point I threw out the render_card
filter approach and started working on a $partial
template expression:
$partial(#card_id, {param1: value1, param2: value2, …})
Here is an example showing how it works within a $foreach
, and passing params to the card being included so that it can customise its own content:
@card c_char_list {
bindings: [
characters: *game.characters
]
content: ```
<h2>Cast of characters</h2>
$foreach(c:characters) {%
$partial(#c_char_desc, {target: c})
%}
```
}
@card c_char_desc {
bindings: [
name: *params.target.name
]
content: ```
<div class="box">${name}</div>
```
}
You can also pass a bound expression instead of an #id
so if our character had something like:
@actor gubbins {
template_id: #c_main_actor
}
then you could use something like:
@card c_char_list {
bindings: [
characters: *game.characters
]
content: ```
$foreach(c:characters) {%
$partial(c.template_id, …)
%}
```
and dynamically specify the template at the same time, allowing different characters to be rendered with different partial templates.
I’m actually a little surprised how easy this was to implement, I hate looking at the renderer code, and the template compiler even more so, but it was actually fairly straightforward and worked first time.
With luck this is the last change I need to make to the template system and rendering code.
This will be in v1.2.10.