Assume I have a shadow tiddler where user is not recommended to touch it. For example
<$list filter="[tag[Learning]]">
</$list>
I wish to pass an extra user defined filter (stored in a tiddler or variable) to further process the first filter. The simple solution is to use subfilter, so if the original code is written like below
<$list filter="[tag[Learning]] :and[subfilter<user-filter>]" >
</$list>
This works fine, but fails if user-filter is not defined. One work around is to use defualt value like [all[]] but I am looking for better solution.
In summary: how can I add a user defined filter run (may have many step) to a predefined filter?
Note: The above example can be tested on tiddlywiki.ocm
download User-Defined-Filter-Extra-Filter.json (392 Bytes)
and open Test My-Template, try to change the user-filter
An old school solution is to wrap the code inside a macro and pass extra filter like below
\define test-extra-filter(user-filter)
<$list filter="[tag[Learning]] $user-filter$" >
</$list>
\end
This way, user-filter can be undefined. But I consider this a bad practice.
You can also use below macro which is better than above
\define test-extra-filter(user-filter:"[all[]]")
<$list filter="[tag[Learning]] :and[subfilter<__user-filter__>]" >
</$list>
\end
BUT, what we can do without using a macro?
xcazin
March 8, 2023, 2:06pm
3
Hi @Mohammad ,
Since we can’t directly provide the else operator with a [subfilter[]] operand, we can reverse the logic thanks to the map:flat operator:
<$let user-filter="[all[]prefix[How]]" default-filter="[all[]]">
<$list filter="[<user-filter>!is[blank]else<default-filter>]
:map:flat[all[tiddlers+shadows]tag[Learning]subfilter<currentTiddler>]" >
</$list>
</$let>
First we decide which filter will be used, then we map all the input tiddlers on the chosen filter.
xcazin
March 8, 2023, 2:16pm
4
Or maybe more generic:
<$let user-input= "[tag[Learning]]"
user-filter= "[all[]prefix[How]]"
default-filter="[all[]]">
<$list filter="[<user-filter>!is[blank]else<default-filter>]
:map:flat[all[tiddlers+shadows]subfilter<user-input>subfilter<currentTiddler>]" >
</$list>
</$let>
Alvaro
March 8, 2023, 7:00pm
5
As @xcazin had pointed you would need to wrap your list inside of the declaration of the variable user-filter.
In this cases $set widget is useful. In your example, it would be something like:
<$set name="user-filter" filter="[{filterTiddler}]" value={{filterTiddler}} emptyValue="[]">
<$list filter="[tag[Learning]] :and[subfilter<user-filter>]" >
</$list>
</$set>
Where filterTiddler would be the tiddler that stores the filter expression.
Thank you @xcazin . Both works specially for cases I want where a modifier operator is used.
Thank you @Alvaro .
I just modified the filter="[{filterTiddler}]" to filter="[subfilter{filterTiddler}]"
Your solution works and it can accept more complex filter runs as it uses a separate run through :and
One cavet is with :and it forces its input as final output! so, if user gonna filter the selection (here [tag[Learning]] he/she should take care to not produce extra titles through filterTiddler.
Alvaro
March 9, 2023, 1:38pm
8
Why? The filter is only for check that the filter outpot isn’t empty. Then the filter expression will be used has paramenter of subfilter operator.
Alvaro:
Why?
The reason is to have code working when filterTiddler is missing or filterTiddler is empty!
Alvaro:
emptyValue="[]"
I think this also should be emptyValue="[all[]]" to pass the previous result intact when nothing is specified by user!
Alvaro
March 9, 2023, 3:08pm
11
My mistake, I didn’t specificy correctly the doubt. The doubt is around of use of subfilter in filter="[subfilter{filterTiddler}] but now I see it the same that a tricky version of [[filterTiddler]!is[missing]!is[blank]]