Proper method for "if and only if list entries exist, display the list"

Hi guys,

Happy fourth of July weekend for those who celebrate it!

What is the proper way, at this point, to have a listwidget, so that, if list entries exist, the list appears, but if no entries exist, the list does not appear? In Stroll there is a reveal widget for this. But a) people are talking about the reveal widget becoming deprecated, and b) I plan to use a number of these in a template. So the less messy and more compact and concise, the better.

Use case, show brainstorming entries from today, sorted by category, but where only the categories with entries are revealed:

tiddler title: 2025-07-05

text description:

If there exist tiddlers tagged with current title and tagged family, show list header, and transclude entries below header. If not, nothing gets displayed.
If there exist tiddlers tagged with current title and tagged health, show list header, and transclude entries below header. If not, nothing gets displayed.
If there exist tiddlers tagged with current title and tagged finances, show list header, and transclude entries below header. If not, nothing gets displayed.

Text output:

Family
Entry 1
Entry 2

[no entries for health, so it doesnt show]

Finances
Entry 1

There are a couple of ways you could do this, but I think it’s easiest to use the conditional <% if %> syntax, which is simplest “modern” replacement for $reveal. For instance:

<% if [tag<currentTiddler>tag[Family]] %>

! Family
<$list filter="[tag<currentTiddler>tag[Family]]" />

<% endif %>

You’ll notice that this has us running the same filter twice: once in the conditional (to determine whether anything is displayed at all), and once to enlist the results, if they exist. Your wiki’s tags are automatically indexed for faster filtering, so this shouldn’t be a terribly resource-intensive task. But if you were using a more complicated filter, you might prefer not to run the whole thing twice!

Luckily, the conditional syntax has a special property: its first result is automatically assigned the variable <<condition>>. (This is because <% if %> is actually just a prettier wrapper for a specific $list — happy to expand more on that if you’re curious.) We can take advantage of this property by using +[format:titlelist[]join[ ]] to save all the results of the filter as a single, space-separated title list, which gets assigned to <<condition>>…

<% if [tag<currentTiddler>tag[Family]] +[format:titlelist[]join[ ]] %>

! Family
<$list filter="[enlist<condition>]" />

<% endif %>

… and then use [enlist<condition>] in the $list widget to return that title-list string to a list of separate titles. If the initial filter is complex, this can be significantly more performant.

Finally, if you want the same output for each category, and the target tag is the only thing that varies between lists, you might want to move this code into a procedure so you don’t have to paste it 3+ times. This should also make it easier to change the template/styling in the future, since you’ll only need to update the procedure definition. So here’s my final recommendation:

\procedure list-category(tag)
<% if [tag<currentTiddler>tag<tag>] +[format:titlelist[]join[ ]] %>

! <<tag>>
<$list filter="[enlist<condition>]" />

<% endif %>
\end

<<list-category Family>>
<<list-category Health>>
<<list-category Finances>>
<<list-category "This tag has spaces!">>

P.S. The above code assumes that your category tags are already capitalized. But perhaps they’re not… and perhaps you’d still like your headers capitalized. In that case, you can style them with the following CSS, either present in the tiddler where you intend to use the procedures or added (minus the <style></style> tags) to a stylesheet of your choice:

<style>
.capitalize { text-transform: capitalize; }
</style>

Then modify your header to include this new class (along with any other styling you prefer):

!.capitalize <<tag>>

The nice thing about this approach is that it doesn’t change the underlying value of <<tag>>, just the way it’s displayed. This means that if you wanted the header to link to the category tag, for instance, you could do so without issue:

!.capitalize <$link to=<<tag>> />

P.S the second: Just realized you said “transclude entries”, not “link to entries”. In that case, you could modify the $list template:

<$list filter="[enlist<condition>]">
	<$link /> <!-- if you want a direct link to the tiddler as well -->
	<$transclude $mode=block /> <!-- or add $field=field-name if you want to transclude a field other than the main text. Either way, I recommend using $mode=block to preserve linebreaks and other block formatting. -->
</$list>
4 Likes

Thanks @etardiff / Emily! I am going to add this to my documenting TW file.

I tinkered with your “final recommendation” before I saw the rest of your post with the transclusion. And I figgered it out myself. Here is my final version>

\procedure list-category(tag)
<% if [tag<currentTiddler>tag<tag>] +[format:titlelist[]join[ ]] %>

<b><<tag>></b><br>
<$list filter="[enlist<condition>]"><span class="indent1"><$transclude mode="inline-block"/>&nbsp;<$link>*</$link></span></$list>

<% endif %>
\end

<<list-category "Better me general">>
<<list-category Family>>
<<list-category Health>>
<<list-category Finances>>
<<list-category Fun>>
<<list-category Giffmex>>
<<list-category Learning>>
<<list-category Organized>>
<<list-category Resonate>>
<<list-category Social>>
<<list-category Spanish>>
<<list-category Spiritual>>
<<list-category Support>>
<<list-category TiddlyWiki>>
1 Like

Looks great! I figured you could handle your own tinkering; I just wanted to explain the process for the benefit of any newbies reading the thread. :wink:

One note: $transclude doesn’t support an inline-block mode (though it might make sense if it did), so I think <$transclude mode="inline-block"/> will get parsed as <$transclude/> and fall back to the default parsing mode — probably mode=inline, since you don’t have any extra linebreaks.

1 Like