Don’t worry, no one understands null
, even its inventor.
$list
widgets are generally used to iterate over a set of items that are identified by the syntax contained in the filter="..."
parameter. For each item, the $list
widget sets the “currentTiddler” variable to contain the current item value so that the wikitext within the body of the $list
widget can to refer to <<currentTiddler>>
in order to display or act upon each list item.
However, this can sometimes be a problem if the wikitext needs to also refer to the title of the tiddler in which the $list
widget itself occurs. To avoid this, the $list
widget allows you to declare a different variable name to be used to hold the current list item value, which leaves the value of <<currentTiddler>>
unchanged.
But… in addition to iterating over a set of items, the $list
widget syntax can also be used as a kind of conditional test where it results in either 0 or 1 items being returned by the filter. If no items are returned, then the wikitext within the $list
body is not processed, but if an item IS returned then the $list
body is processed. In this situation, it usually doesn’t matter what variable the $list
widget uses, so the default “currentTiddler” variable is typically set, but NOT referenced.
However, as noted above, it is sometimes still necessary to reference the title of the tiddler in which the $list
widget itself occurs, so we don’t want to change the value of <<currentTiddler>>
and need to add the variable="..."
syntax to avoid this. But that variable isn’t really needed within the $list
body, so any name will do. Some people use variable="null"
for this purpose. Within my own code, I personally prefer using variable=none
. Also note that I omit the quotes around the variable name, as they are not required for the syntax to be parsed correctly.
Hope this explanation is clear…
enjoy,
-e
This is a very reasonable stance, and I don’t want to undermine it; I took a pretty similar trajectory myself. That’s part of the reason why I do spend so much time encouraging people to consider a template; the tiddlers I made early on contained a lot of repetitive HTML and other styling/templating content, and it was tedious to remove it all once I had learned more efficient techniques.
I’d alluded to this issue with changing startText
in my first response, but in retrospect I wasn’t particularly clear about the specific pitfalls. I’m glad you were able to work through the problem to your satisfaction; I’m sorry you had to spend those hours investigating!
You’re spot-on in your conclusions about filtered attribute values. If you were using {{{ [{!!text}splitregexp[\n]!is[blank]] }}}
as stand-alone wikitext, you would need to add first[]
or nth[1]
to the end of the filter in order to restrict the results to the first line only.
In fact, you could also use in it summary={{{ [{!!text}splitregexp[\n]!is[blank]first[]] }}}
without affecting the results. It’s not necessary from a functional perspective, but I encourage you to do so if it makes the code easier for you to parse at a glance; it shouldn’t add any meaningful lag to the rendering process.
@EricShulman’s already provided an excellent explanation of the theory behind variable="NULL"
, so I’ll just add that it’s not strictly necessary in the code I suggested. Since I defined the <<summary>>
and <<text>>
variables outside the $list, those values won’t change even if the value of <<currentTiddler>>
changes.
However, if you did want to include a field transclusion (let’s say {{!!modified}}
) in your $list template, you’d want to ensure that you were seeing the value of that node’s modified
field, not the modified
field of the (likely nonexistent) tiddler whose title corresponds to the value of <<text>>
. And in that case, you’d want <<currentTiddler>>
to retain its original value (i.e., the title of the node tiddler). So I included variable="NULL"
as a bit of future-proofing, to minimize potential issues if you decided to further expand on the template.
However, this can sometimes be a problem if the wikitext needs to also refer to the title of the tiddler in which the
$list
widget itself occurs.
Based on the template snippet above, is it correct to think that this usually happens in context of nested lists, because there’s a clash between items from either in the <<currentTiddler>>
variable, and the workaround is the outer list defining another variable, to isolate the contexts? Or is there the presence of a nested list not necessary, and there still are situations when this custom variable might be needed?
As a bit of a side note… You’ll see a lot of similar uses of $list as a conditional test in older TW content, but if you’re using TW 5.3.2+, the <% if %>
conditional shortcut syntax may be a more semantic alternative. Here’s an alternate version of the same template using conditional shortcuts instead:
<$let
summary={{{ [{!!text}splitregexp[\n]!is[blank]] }}}
text={{{ [{!!text}removeprefix<summary>trim[]] }}}
>
<% if [<text>!match[]] %>
<details>
<summary><<summary>></summary>
<$list filter="[<text>]"><$transclude field=title mode=block /></$list>
</details>
<% else %>
<$transclude mode=block />
<% endif %>
</$let>
You can see that I’m not setting a “placeholder” variable here, and that’s because <% if %>
does so automatically: it assigns the first result of the filter to the variable <<condition>>
, which you can use inside the conditional as needed (or ignore if it’s unnecessary in your template code). This means that <<currentTiddler>>
will retain whatever value it had outside the conditional without any special effort on your part.
In some ways, this is a more elegant solution, and I did consider it. But in testing my code on the Streams site, I realized that the Streams wiki itself is still using v. 5.2.2, so I opted for the more “old-fashioned” approach. Digging a little further, I now realize that the plugin itself requires >=5.1.23, which predates the introduction of the $let widget… so I suppose if I really wanted to cover my bases, I should have used the older $vars widget instead.
You’re right that we generally assign $list variables to avoid <<currentTiddler>>
clashes, though they don’t always happen in the context of nested lists at the tiddler-text level: you might want to retain your ability to transclude field values from the current tiddler inside a single $list. Of course, once you start digging around in the $:/core
, $lists are responsible for a great deal of the TiddlyWiki UI (including each tiddler in the story river), so in a way we’re always dealing with nested lists.
I also find it’s sometimes worth it to assign a variable to a list even in circumstances where I don’t mind <<currentTiddler>>
changing simply because it lets me use that <<variableName>>
to reference the current list-item inside the $list template — even when <<currentTiddler>>
or its synonym {{!!title}}
would technically do the job.
For instance, the following…
<$list filter="[[Lewis Carroll]] [[Terry Pratchett]] [[Isaac Asimov]]" variable=author>
!! <<author>>
<<list-links "[tag[Book]author<author>]">>
</$list>
… may be clearer at a glance than this simpler code, which produces the same results:
<$list filter="[[Lewis Carroll]] [[Terry Pratchett]] [[Isaac Asimov]]">
!! {{!!title}}
<<list-links "[tag[Book]author{!!title}]">>
</$list>
In the core we use variable="ignore"
for these type of variable names. It is not perfect but consistently used that way.
In the core we generally use quotes around variable values for two reasons:
A) Consistency and
B) It is less error-prone if users do copy paste core code for their own use-cases
Sometimes users need values, that contain spaces. If the core code does not contain quotes they think we can handle “values with spaces” without the quotes – Which is an error.
The future proof way to do this in terms of making future upgrades easier is to use a separate tiddler with the tag $:/tags/streams/user-actionmacros
, see Streams — on TiddlyWiki 5.2.2
I was waiting for MWS to fix a lot of these “early errors” I made when learning.
Is there any similar system for the row-body template or the context menu, I wonder? I have found that those have really unlocked the potential of Streams for me, action-macros being just the gateway
@well-noted I am very pleased to see how you have been adapting Streams to fit your own use cases. It was intentionally written to be flexible and adaptable but not many users that I am aware of have gone down that route.
The context menu is extensible as well, however I think you have shown that the customization options are not sufficient in terms of conditional display. I think the logical extension here would be to add a condition field to each context menu entry that contains a filter and must evaluate to a non-empty value in order for the entry to be shown for any given node. One could also consider adding support for transcluding entire tiddlers in the context menu, based on a given tag.
With regards to $:/plugins/sq/streams/templates/stream-row-body, overriding it is indeed the recommended approach. I have shied away from adding tag or cascade based customization options as in very large streams of nodes, those lookups can potentially lead to a significant performance impact in larger wikis. Instead, the default template is intentionally very simple and users can customize it to fit their needs. It would be interesting to revisit this at some point to ascertain whether the performance concerns are still valid, given the improvements to the TiddlyWiki core and browser engines since the last major rewrite of Streams in 2021.