Tips and commentary - Notes on tiddlers stored in a data tiddler

The following is a TiddlyWiki script solution to place multi-line notes on any tiddler, with wikitext, but stored in a data tiddler. This means the tiddler on which the note is added is not touched.

It has been written not to demonstrate the perfect solution, but to demonstrate a range of tips and tricks. If you work through the notes you are likely to learn a lot, and if not you have a subject on which to raise a trick you have found.

  • This is a little “Sharing and Education Project” of TW_Tones before he leaves a place with reliable internet.
  • Please provide feedback and I may publish more along this line in future.

If you do like the solution,

  • put the procedure in a tiddler with the $:/tags/Global so its available everywhere
  • Then either
    • put the macro in tiddlers you want notes on
    • or put it in a tiddler with the tag $:/tags/ViewTemplate perhaps with an empty list-after to force it to the bottom of all tiddlers.
      • You could wrap it in a conditional to display only on select tiddlers eg [all[current]is[tiddler]]
    • You can even tag it with $:/tags/EditTemplate to see it in the editor. However it will be a different note because when you edit a tiddler it will be in draft mode.
    • Unless its modified to test if has[draft.of] and use draft.title
  • Warning if the tiddler is renamed you will no longer see the notes for it. Do have an idea how to fix this?
    • We may make a way to view even lost notes

The code, Paste into a tiddler and test the button

\procedure view/edit-notes()
   \function note.state() [<qualify $:/temp/edit-notes>]
   \function get.note() [<note-database>getindex<currentTiddler>]
   \define note-database() $:/note-database

   <$button popup=<<note.state>> tooltip="view/edit-notes for this tiddler">
      <%if [<note.state>has[title]] %>View Note<%elseif [<get.note>!is[blank]] %>Edit Note<%else%>New note<%endif%>
   </$button>

   <%if [<note.state>has[title]] %>
      <$edit tiddler=<<note-database>> index=<<currentTiddler>> class="tc-edit-texteditor tc-popup-handle" focus=yes/>
   <% elseif [get.note[]!is[blank]] %>
      <$button tooltip=`Delete note for "$(currentTiddler)$" permanently - Saved to clipboard` 
          message="tm-copy-to-clipboard" param=<<get.note>> 
          actions='<$action-setfield $tiddler=<<note-database>> $index=<<currentTiddler>> $value=""/>' 
          class="tc-btn-invisible">
          {{$:/core/images/delete-button}}
       </$button>
      <$button tag=fieldset style="padding: .5em;" popup=<<note.state>> tooltip="Click to edit">
         <$transclude tiddler=<<note-database>> index=<<currentTiddler>> mode="block" />
      </$button>
   <%endif%>

\end view/edit-notes

<<view/edit-notes>>

If you paste the above in a tiddler you can see how it works on that tiddler. perhaps write notes as you read through the rest below, which references code snippets from the above.

First the procedure

\procedure view/edit-notes()
   ...
\end view/edit-notes

<<view/edit-notes>>
  • in this example we define a procedure, and end/close the procedure with its name. This helps identify the end of the procedure. It also permits other “pragma” to define things within the procedure
  • We then invoke it with the short name, we need no parameters because the values it uses are either already set or defined inside the procedure
  • Note the use of a / in the name, the Procedure Definition Syntax tells us;

The procedure-name is a sequence of non-whitespace characters other than ( or >.

Although the following may suggest limits that don’t seem to apply

The procedure-name is a sequence of letters (AZ, az), digits (09), hyphens (-) and underscores (_)

Notice how I indented everything here by 3 characters or more? This allows us to quickly scan what makes up the whole procedure

Because we close the procedure with \end view/edit-notes we can include pragma within it, but only at the top, as in tiddlers themself.

  • Pragma defined this way are only visible inside the procedure they are defined, so you can reuse any name you want.
   \function note.state() [<qualify $:/temp/edit-notes>]
   \function get.note() [<note-database>getindex<currentTiddler>]
   \define note-database() $:/note-database
  • The first function note.state is a filter that returns a single title, my own informal standard is to separate words with a period . helping me tell the difference between macros/procedures/variables from when I use a function. This is used to toggle the existence of the state tiddler with the popup parameter on the button.
    • A period in a function name is also required if you want to use it as a custom operator inside filters
    • In this function we call a tiddlywiki macro, qualify, one that is defined by the system as a JavaScript macro, and its parameter.
  • The second function get.note retrieves a record from the note database using the currentTiddler’s title. If it retrieves nothing there is no note. Notice how it uses the note-database (macro/variable) that follows. the order of pragma is not important here.
  • The third pragma is a \define and whilst deprecated, you can still make use of them. I tend to use them to define “static” unchanging values. It would work the same if made into a procedure. We could have used a set/let or vars, but then we would need to close those eg </$vars>.
    • By turning this into a macro/variable you can change the value here and the procedure will continue to work, you don’t need to look everywhere it is used

Now the first tiddlywiki script after the pragma is;

   <$button popup=<<note.state>> tooltip="view/edit-notes for this tiddler">
      <%if [<note.state>has[title]] %>View Note<%elseif [<get.note>] %>Edit Note<%else%>New note<%endif%>
   </$button>

Here we define a button using the popup parameter, this parameter creates and deletes the tiddler with the title in <<note.state>> with subsequent clicks. This is one of the simplest ways to change/toggle the state of something, if not the simplest. From now we can test the existence of the <<note.state>> with [<note.state>has[title]] as we do here;

Lets us consider what is inside this very simple button; I will add line breaks to make it easier to read and it makes no difference to the way the procedure operates.

<%if [<note.state>has[title]] %>
   View Note
<%elseif [<get.note>!is[blank]] %>
   Edit Note
<%else%>
   New note
<%endif%>
  • <%if [<note.state>has[title]] %> %If the note.state tiddler exists (as discussed earlier) it displays View Note inside the button and that is the buttons name as displayed.
    • In this case the button tells you what will happen if pressed, so this will appear only when editing a note.
  • We could use else here but by using %elseif we can ask another question if the original %if is false. Here we ask [<get.note>!is[blank]] this will return the note content if it exists, but this can also be used if to detect if there is “no note” (the else condition that follows). The !is[blank] guarantees to exclude some cases where its blank but not empty.
    • So if it has a value it will display Edit Note inside the button and that is the buttons name as displayed.
      • In this case the button tells you what will happen if pressed, so this will appear only when Viewing a note.
    • We could close this if/elseif now with an endif but lets display a different button name if it is empty. New Note.

So that is our toggle button and the text it displays is dependent on both the note.state and the existence or otherwise of the note.

  • You could pull this out and use it separately but remember it makes use of the \pragma above it

Now lets consider the rest of the procedure;

  • observe it consists of one big %if and ends with an %endif, thus everything is conditional according to the filters therein
   <%if [<note.state>has[title]] %>
      <$edit tiddler=<<note-database>> index=<<currentTiddler>> class="tc-edit-texteditor tc-popup-handle" focus=yes/>

   <% elseif [get.note[]!is[blank]] %>

      <$button tooltip=`Delete note for "$(currentTiddler)$" permanently - Saved to clipboard` 
          message="tm-copy-to-clipboard" param=<<get.note>> 
          actions='<$action-setfield $tiddler=<<note-database>> $index=<<currentTiddler>> $value=""/>' 
          class="tc-btn-invisible">
          {{$:/core/images/delete-button}}
       </$button>

      <$button tag=fieldset style="padding: .5em;" popup=<<note.state>> tooltip="Click to edit">
         <$transclude tiddler=<<note-database>> index=<<currentTiddler>> mode="block" />
      </$button>

   <%endif%>

‘‘First.’’ %if our note.state tiddler has a title, a note already exists, the button above reads Edit Note, then we use the edit-text widget. We will cover that in more details soon.

<$edit tiddler=<<note-database>> index=<<currentTiddler>> class="tc-edit-texteditor tc-popup-handle" focus=yes/>

‘‘Second’’ %elseif we know the note.state tiddler has NO title and the condition now is [get.note[]!is[blank]] which is equivalent to [<get.note>!is[blank[]] (showing how functions can be used as a custom “filter operator” if it includes a . in the name).

  • So there is no state tiddler (not editing the note) but we do have a note. Lets view it until someone selects a button to edit it.

Viewing the note

  • ''Firstly ‘’ we display a delete button, which will set the value of a note to empty, thus deleting the note, more on it soon.
  • ‘‘Secondly’’ Lets view the note, Lets use a button to display the content of the note using the $transclude widget to access the note, with mode block to process the wiki text within.
    • The button has the tag=fieldset, which will not display like a button, but as if the content was wrapped in a <fieldset><.fieldset> tag, this results in a boarder around the content and a tooltiip to suggest you can click on the view to edit. As we again use popup=<<note.state>> clicking on the view of the note will also toggle the state tiddler again, Just like the above Edit Note button.

The Edit widget

      <$edit tiddler=<<note-database>> index=<<currentTiddler>> class="tc-edit-texteditor tc-popup-handle" focus=yes/>

In this case we provide the note-database as the tiddler to edit, but use an index of the currentTiddler to find our specific note.

  • The class=“tc-edit-texteditor tc-popup-handle” provides some standard css for the edit field, and tc-popup-handle stops the edit closing when you click on it (thanks Eric)
  • the focus=yes will select the text inside the editor, or put the cursor there if empty.

The delete button

      <$button tooltip=`Delete note for "$(currentTiddler)$" permanently - Saved to clipboard` 
          message="tm-copy-to-clipboard" param=<<get.note>> 
          actions='<$action-setfield $tiddler=<<note-database>> $index=<<currentTiddler>> $value=""/>' 
          class="tc-btn-invisible">
          {{$:/core/images/delete-button}}
       </$button>

This button is more complicated than the others, because when we delete things it is a destructive act. Let us consider the parameters separately;

tooltip=`Delete note for "$(currentTiddler)$" permanently - Saved to clipboard` 

We use a tooltip here to warn someone who puts the mouse on top of the button, they see the tooltip

  • We use “single backticks otherwise” called Substituted Attribute Values this allows us to concatenate literal text with the value of the current tiddler (substitutes the currentTiddler variable).
  • There are various ways to trigger actions on a button click, after all that is there main use. In this case we use two, sendmessage and actions (we used popup before, and actions can be inside a button, but are set in stone at the moment the button is rendered, variables etc do not update)
    • Before we do anything we copy the current note to the clipboard, allowing an accidental delete to be restored with a paste back into the note or elsewhere. Maybe you want to add it to the tiddlers content?
message="tm-copy-to-clipboard" param=<<get.note>> 

Now the actions parameter

          actions='<$action-setfield $tiddler=<<note-database>> $index=<<currentTiddler>> $value=""/>' 

now lets use set field, against the <<note-database>> but use the $index of currentTiddler. By setting it’s $value to "" an empty string, it will be deleted.

  • This could be called inline tiddlywiki script. It uses single quotes to wrap the action so we can use double quotes inside to set the value.

The last button parameter class="tc-btn-invisible" hides the standard button look and we just display the {{$:/core/images/delete-button}} image by transcluding it.

Regards TW_Tones original Author

24 posts were merged into an existing topic: Discussing - Notes on Tiddlers stored in a Data Tiddler