Use variable="transclusion" in a list widget to get unique popups

Here’s a little trick I discovered this evening while answering a question on the discord group.

If we use the tag macro to display tag pills in a list widget, and we get the same tags several times, the popup menu will open for all identical tags. We can prevent this by using a let widget and overriding the value of the transclusion variable, which is used by the qualify macro to generate the unique identifier of the popup.

But we dont even need to use a let widget, we can use the counter attribute instead (edit: use the variable attribute for best performances) ! Each run of the list widget is unique, and thus we will get a unique value for the qualify macro, and therefore, unique popup menus.

E.g :

<$list filter="[range[0],[5]]">

<<tag Test>>

</$list>

Will produce this (when we click on one of the tag pill) :

image

But if we set the counter attribute to transclusion :

<$list filter="[range[0],[5]]" counter="transclusion">

<<tag Test>>

</$list>

We get this :

image


EDIT: Here’s a demo : How to get unique popups in a list widget

3 Likes

Thanks for sharing this useful code pattern, new features like the counter need exposure as solving other issues.

  • @telumire can you explain why setting the transclusion variable works for the tag macro and why you want the same tag pill to display 6 times?

One additional trick to add to this is to use the counter = 1 to trigger a heading. Perhaps not as applicable in this above example; as a rule I call the counter variable “item” and if I have its total “items”.

eg; Heading Inside a list using counter=item

<$list filter="filter" counter=item>
   <$list filter="[<item>match[1]]" variable=~>Heading if 1 or more items satisfy filter</$list>
<!-- Each item below -->

</$list>
  • In the above the heading will only appear if there is one or more items
  • If before hand you count the total number of items you can also test for the last item to add a footer to the list.
<$set name=items filter="filter count[]]">
<$list filter="filter" counter=item>
   <$list filter="[<item>match[1]]" variable=~>Heading if 1 or more items satisfy filter</$list>
<!-- Each item below -->

<!-- footer below -->
   <$list filter="[<item>match<items>]" variable=~>Footer after all items,  if 1 or more items satisfy filter</$list>
</$list>
  • Observe the plural of item is items (ie total number of items)

Nice catch. … But you need to be careful. Using the counter in lists can have performance implications.

Read the docs :wink: https://tiddlywiki.com/#ListWidget

I see … it’s possible to use the variable attribute instead, it should also works I think ?

Hi @telumire

That’s an excellent trick, and should work well for a number of common usecases.

For those who don’t know, the heart of this trick is the “transclusion” variable. The core automatically assigns it within transclusions, giving it a value made up of the name of the target tiddler/field/index combined with the “currentTiddler” variable.

The “transclusion” variable is then used by the <<qualify>> macro in a subtle way: the magic numbers in the result of the macro are a hash of the concatenation of all the values of the transclusion variable assigned going back through all the ancestors of the current node.

That’s why the solution to not having unique values returned by the <<qualify>> macro is generally either to introduce a transclusion or an assignment to the “currentTiddler” variable.

The trick here is that using it as a list counter is a convenient shorthand for directly assigning a unique value to the “transclusion” variable for each list item.

2 Likes

There’s a marginally easier way to accomplish this. As the docs notes, the name of the counter variable with -first or -last appended is set to yes or no as appropriate. So your example can be accomplished with:

<$list filter="filter" counter="item">
   <$list filter="[<item-first>match[yes]]" variable="ignore">
      Heading if 1 or more items satisfy filter
   </$list>
   <!-- Each item below -->
</$list>

It’s more of a help when one is trying to detect the last item; the alternative requires the number of items to be precomputed.

1 Like

It’s worth giving a bit more detail:

“Using the counter in lists can reduce performance when working with list items that dynamically reorder or update themselves”

We really should not discourage people from using list counters. For the situations where a counter is required, it will always be more efficient to use the built-in counter rather than trying to work around it in other ways. So the best advice we should give is much simpler: don’t use the counter attribute unless you really, really must. If you can’t see an easy alternative then there probably isn’t one, so just go ahead and use it.

1 Like

Yes I think it should work, and that should probably be the recommended approach in most situations.

It’s also worth noting that using the template attribute already has the same effect because it transcludes the template tiddler for each item in the list.

It’s just a way to simplify the demonstration of this trick, see this link for a more complete demo, it should be clearer :

https://demos.tiddlyhost.com/#Unique%20popups%20in%20the%20same%20tiddler

Here’s the code for the tag macro (it’s defined in $:/core/macros/tag):

\define tag(tag)
{{$tag$||$:/core/ui/TagTemplate}}
\end

If you look into $:/core/ui/TagTemplate, you’ll see that it uses the qualify macro, and the qualify macro use the transclusion variable (see @jeremyruston’s excellent explanation).

That is true, but it would require a separate tiddler (which is fine; but I like self-contained solutions :grin:).

Another way to achieve that is to use a let (or set, vars…) widget, would that be better for performances?

<$let transclusion=<<currentTiddler>> ><<tag>></$let>

No. … I think using the transclusion variable will be the approach to go for the usecase mentioned in the OP. The list-widget basically does the same thing as the let-widget in your example.

I just want to share that most of the times I use the list widget I make it operate on the current tiddler variable so then when I use the tag template or <<tag tagname>>it is qualified to the current tiddler and the qualification works well.

There’s the usecase, where the default setting fails: Demos — Answers and demos for questions about tiddlywiki and using the “transclusion” variable fixes the problem.

1 Like

Very useful example! It clarifies the issues a lot in a very neat way!
Was that made by @telumire?

TT

1 Like

Yes, I intend to use this from now on when I want to provide an answer relying on several tiddlers / provide interactive examples :slight_smile:

2 Likes

Also see this discussion: Link styles of all tags based on filter - #8 by EricShulman