Using a select widget to add or remove tags to a tiddler

Hello all! Long time no see… work prevented me from making much progress for a while on my GURPS TW project, but I finally got back to it… and having become rusty, I’m already struggling :stuck_out_tongue_winking_eye:

Here’s a short context: I have skill tiddlers that can belong to one or more Skill Categories. When such a skill tiddler is edited, I want to provide a select menu listing all the skill categories (a skill category is a tiddler tagged with GURPS Skill Category). I wrote the two procedures below to do that, but I currently have two issues I can’t seem to fix:

  1. The addition of the checkmark character (:heavy_check_mark:, the html code ✔ below), for categories already present in the tags list of the skill, doesn’t work - it appears on ALL categories, regardless of their absence from the tags. I tried doing this bit a logic with and conditional rather than a nested list, but the checkmark still appears on every category.
  2. The deletion of the temp tiddler doesn’t work in my skill-categories-select-actions procedure; its purpose being to reset the display of the selected category to its default blank, so that you can toggle tags on and off the skill easily (currently, the toggle works, but to remove a tag previous added through the select widget, one has to add another category tag, then remove the first one, then remove the extra that was added)

(I linked the TW in the first paragraph with the skill macros tiddler where those two procedures below reside, and the Animal handling skill example to edit, if you want to take a look at the behaviour I describe)

Any help appreciated! Thanks in advance, I’m sure it’s something rather simple that I’m missing…

\procedure select-skill-categories()
<$select tiddler="$:/temp/generated-list-skill-categories-state"
	default=""
	actions=<<skill-categories-select-actions>>
>
	<$list filter='[tag[GURPS Skill Category]]-[prefix[All]]'><option value={{!!title}}>{{!!title}} <$list filter='[<currentTiddler>search:tags::<selected>]'>&#x2714;</$list></option></$list>
</$select>
\end

\procedure skill-categories-select-actions()
<$let
	current={{{ [<currentTiddler>search:tags:[skills]] }}}
	selected={{$:/temp/generated-list-skill-categories-state}}
	existing={{{ [[tag[GURPS Skill Category]]-[prefix[All]]] }}}
>
<$action-listops $tags="+[toggle<selected>]">
<$action-deletetiddler $tiddler="$:/temp/generated-list-skill-categories-state" /></$let>
\end

There are a few issues in your current code, but the first I noticed is that you define the <<selected>> variable in skill-categories-select-actions, not select-skill-categories, so it’s undefined in the context of the <option> where you’re trying to invoke it.

  • An undefined variable is treated as an empty value, so [<currentTiddler>search:tags<selected>] gets parsed as [<currentTiddler>search:tags[]]. search treats all strings as including the null string “”, which means that the list condition is always true and the checkmark is always displayed.
  • However, I don’t think you really want the checkmarks to appear based on the <<selected>> variable (i.e., the selection you’re making with the $select widget — which will be empty 99% of the time, except in the instant after you make the selection and before the temporary tiddler is deleted), but based on whether the current tiddler already has that tag, so you’ll to rework some filters in any case.

Here’s a revision you can try:

\procedure select-skill-categories()
\define temp() $:/temp/generated-list-skill-categories-state
<$select tiddler=<<temp>>
	default=""
	actions="<<skill-categories-select-actions>>"
>
	<$list filter='[tag[GURPS Skill Category]!prefix[All]]' variable="tag">
		<option value=<<tag>>><<tag>> <% if [<currentTiddler>tag<tag>] %>&#x2714;<% endif %></option>
	</$list>
</$select>
\end

\procedure skill-categories-select-actions()
<$let
	selected={{{ [<temp>get[text]] }}}
>
<$action-listops $tags="+[toggle<selected>]" />
<$action-deletetiddler $tiddler=<<temp>> />
</$let>
\end

Changes:

  • Removed the current= definition — you didn’t seem to be using it anywhere?

  • Removed the existing= definition. You weren’t using <<existing>> anywhere; you do use [tag[GURPS Skill Category]]-[prefix[All]] once, but only once, so there’s no benefit to defining it.

    • Additionally, when you define an attribute with {{{ ... }}}, only the first result of the filter is saved as the value.
      • To save the full results of the filtered translusion as a variable, you need to first convert the results into a title list separated by spaces: existing={{{ [tag[GURPS Skill Category]] -[prefix[All]] +[format:titlelist[]join[ ]] }}}
      • Then use [enlist<existing>...] (not simply [<existing>...]) to convert this single string back to a list of discrete titles when you use the variable in a subsequent filter.
    • However, since you only use the filter once, there’s no real benefit to using the format/enlist steps here!
  • Cleaned up some extraneous : in your search suffixes and some extra square brackets (as in [[tag[GURPS Skill Category]]-[prefix[All]]], where you have an extra bracket at both the beginning and the end of the filter.)

    • Replaced [tag[GURPS Skill Category]]-[prefix[All]] with [tag[GURPS Skill Category]!prefix[All]] — this is slightly more efficient as it only filters out tiddlers with the All prefix and the appropriate tag, rather than looking for every All... title in your wiki.
  • Defined a <<temp>> variable — this makes your code slightly shorter, as you were using $:/temp/generated-list-skill-categories-state in multiple places.

And for your list of options specifically…

	<$list filter='[tag[GURPS Skill Category]!prefix[All]]' variable="tag">
		<option value=<<tag>>><<tag>> <% if [<currentTiddler>tag<tag>] %>&#x2714;<% endif %></option>
	</$list>
  • I’m using variable=tag to assign the results of the filter to the variable <<tag>>; otherwise, they would be assigned to the <<currentTiddler>> variable, and <<currentTiddler>> would no longer point to the skill tiddler whose tags you’re actually modifying.
    • This means I also had to replace <option value={{!!title}}>{{!!title}} with <option value=<<tag>>><<tag>> to display the correct text.
  • I replaced [<currentTiddler>search:tags<selected>] with [<currentTiddler>tag<tag>].
    • You’re defining <<selected>> as the current value of the temp tiddler, but (as discussed above) that’s not the value we’re interested in here. Instead, we want to know whether each $select option (= each tag) is already present on the current tiddler — so I used <tag> instead.
    • I’m using tag<tag> rather than search:tags<tag> because I assume you want the checkmark to appear only when the tiddler has that exact tag, not when it has other tags that may contain the same words.
  • I also replaced the $list in which that filter appears with an <% if %> condition. This is just my semantic preference and doesn’t affect the functionality, so use the $list if you prefer it!

Re: your second issue

I’m not sure how to solve this one Never mind, I found the problem: you were missing the closing / in <$action-listops $tags="+[toggle<selected>]" />, which meant that the widget wasn’t being closed until the /> at the end of the $action-deletetiddler widget, and the $action-deletetiddler wasn’t being parsed at all. I fixed this in my code above and it seems to work as expected.

1 Like

@etardiff to the rescue, once again :slight_smile: Thank you for the clear explanations, including the format/enlist little nugget :slight_smile: