Struggling with Procedures

I thought I would start by creating a simple procedure… I wanted to give it a tiddler name and a field name and have it create a new tiddler combining these two values, eg “Button/TiddlerA/summary”. I can get it to appear in normal text, I can get it to appear on the button itself, but it doesn’t translate to the created tiddler itself. This is my cut down example code:

\procedure 
     EditField(
          tiddler:[all[current]],
          field:"text"
     )

<$list filter=<<tiddler>> variable="focus">
     <$set name='StateTiddler' value='Button/<<focus>>/<<field>>'>
         With this button, I want to create a tiddler called ''<<StateTiddler>>''...
          <$button set=<<StateTiddler>> setTo="yes"> Create <<StateTiddler>> Tiddler... </$button>
          ...but I get [[Button/<<focus>>/<<field>>]] instead   
     </$set>
</$list>

\end EditField

<<EditField tiddler:"TiddlerA" field:"summary">>

I am guessing it is something simple?

Indeed, it’s just one small detail.

You cannot pass complex values to parameters this way value='Button/<<focus>>/<<field>>', because everything between the quotes is interpreted literally as a string.

You can pass a single variable like value=<<focus>>.

If you want to combine multiple variables and strings into a single parameter value, you can do it with filtered transclusion:
value={{{ [[Button/]] [<focus>] [[/]] [<field>] :and[join[]] }}}.
The last filter step :and[join[]] simply concatenates together everything into a single string, otherwise it would be a list.

So your code should look like this:

\procedure 
     EditField(
          tiddler:[all[current]],
          field:"text"
     )

<$list filter=<<tiddler>> variable="focus">
     <$set name='StateTiddler' value={{{ [[Button/]] [<focus>] [[/]] [<field>] :and[join[]] }}}>
         With this button, I want to create a tiddler called ''<<StateTiddler>>''...
          <$button set=<<StateTiddler>> setTo="yes"> Create <<StateTiddler>> Tiddler... </$button>
          ...but I get [[Button/<<focus>>/<<field>>]] instead   
     </$set>
</$list>

\end EditField

<<EditField tiddler:"TiddlerA" field:"summary">>

I tested it on tw-com and the button does produce the Button/TiddlerA/summary:

1 Like

Thank you, I tried to run with your inspiration, and created a new variable, which worked. I then tried to use the same logic in the $action-setfield and it failed me…

\procedure 
     EditField(
          tiddler:[all[current]],
          field:"text"
     )

<$list filter=<<tiddler>> variable="focus">

     <$set name='StateTiddler' value={{{ [[StateTiddler/]] [<focus>] [[/]] [<field>] :and[join[]] }}}>     
     <$set name='TempTiddler' value={{{ [[OldContent/]] [<focus>] [[/]] [<field>] :and[join[]] }}}>
     <$set name='OldContent' value={{{ [[{{]] [<focus>] [[!!]] [<field>] [[}}]] :and[join[]] }}}>

The variable ''OldContent'' should show the contents of the current tiddlers summary field... <<OldContent>>... and it does

          <$button set=<<StateTiddler>> setTo="yes" class="$buttonclass$" tooltip="Edit Field"> 
               <$action-setfield $tiddler=<<TempTiddler>> $field=<<field>> $value=<<OldContent>> /> 
               Click Here
          </$button>

However, the value that action-setfield puts into the summary field within [[OldContent/TestProcedure/summary]] is a reference to ''{{TestProcedure!!summary}'' rather than the actual value itself.
     
     </$set>
     </$set>
     </$set>
</$list>

\end EditField

<<EditField field:"summary">>

I have created a test environment here https://404.tiddlyhost.com/

How about changing the OldContent set widget to:

<$set name='OldContent' value={{{ [<focus>get<field>] }}}>

FYI. You can of course create tiddler s with the action set widget but also tm-new-tiddler and action create tiddler.

Thank you all, this has really helped, although whilst happy to take the answers I am still struggling to understand the theory

Your OldContent $set widget assembles the wikitext for a transclusion, but it doesn’t actually parse the text and substitute the transcluded value when you use the variable in the $action-setfield. To do this, you’d need to add a wikify widget inside the last $set:

<$set name='OldContent' value={{{ [[{{]] [<focus>] [[!!]] [<field>] [[}}]] :and[join[]] }}}>
<$wikify name="WikifiedContent" text="<<OldContent>>">
...
<$action-setfield $tiddler=<<TempTiddler>> $field=<<field>> $value=<<WikifiedContent>> />

But Brian’s {{{ [<focus>get<field>] }}} approach is a much simpler solution—and in fact, you could replace the $set widgets with a single $let widget to make it simpler still:

<$let
	StateTiddler={{{ [[StateTiddler]] [<focus>] [<field>] :and[join[/]] }}}
	TempTiddler={{{ [[OldContent]] [<focus>] [<field>] :and[join[/]] }}}
	OldContent={{{ [<focus>get<field>] }}}
>

(Here, I also moved the / into the join operator to reduce a little redundancy.)

1 Like

@HungryApple

In the spirit of helping you learn;

  • Feel free to ignore now, but this is additional advice, on the code above.

If you dont want the link appearing on the button use Create <$text text=<<StateTiddler>>/>

The summary field contains ''summary'' can I suggest you leave the bold wikitext out of this field, then you get to choose it when you display it, not when you set it. It is always best to leave the formatting outside your data.

''{{!!summary}}'' will bold it later.

But there are even better ways to write the code with tiddlywiki 5.3.x

  • However there is a little more complexity in the code that to me seems unnessasary. It may not be when you put it into other code but it makes a good solution a bit messy.
  • I would like illustrate how to do it another way and if it interests you I can expalin it further but I need a statement of what you want as a result, not infer have to it from your code.

Some tips

  • Make macros and procedures without caps as is easier to remember the name
    • edit-field not Edit-Field as there is no question about capitalisation or not.
  • No need to define focus, just use <<tiddler>>
  • I am not sure why you use the StateTiddler and TempTiddler? I don’t think you need them, but perhaps you have a reason?
  • $buttonclass$ is not valid in procedures only <<buttonclass>>, but buttonclass is not a parameter anyway
  • Rather than define OldContent as {{{ [<tiddler>get<field>] }}} just use it in the button $value={{{ [<tiddler>get<field>] }}}
    • Do the same for <<StateTiddler>> and <<TempTiddler>> if they are needed (I am not sure they are).
    • A common way to set a temp-tiddler is <<qualify "$:/temp/update">> the use the same to retrive it’s value.

When and if the filters are needed, you can define them in a “function” or use ${ filter }$ inside a backtick attribute.

Perhaps return here later, as your learining journey continues.

Just so everyone knows, with v5.3.0, substitution attributes are way better for string concatenation. I’m seeing people suggest stuff like

value={{{ [[Button/]] [<focus>] [[/]] [<field>] :and[join[]] }}}

You can do this instead:

value=`Button/$(focus)$/$(field)$`

No more need to use [join[]].

@Flibbles I totally agree with this approach but using Join is not a problem if it serves another purpose. If you are going to want to compute the save value many times OR make your code read a little easier to read using a function is also helpful.

\function focus-temp() [[Button/]] [<focus>] [[/]] [<field>] :and[join[]]

value=<<focus-temp>>

Sure, I don’t mean to suggest that there is no place for \functions or [join[]], but before v5.3.0, there wasn’t a great way to concatenate together strings for attributes the way @HungryApple needs. For that specific problem, [join[]] can be ungainly, and substitution attributes provide a much cleaner solution.

Thank you all for your fantastic feedback. You inspired me to work on this over the weekend. I have something that works, although I am not sure if it’s the most eloquent solution.

I am writing a procedure to show any tiddler field of my choice with a popup button upon a mouse hover to be able to edit the field directly. This may explain some of the undisclosed buttonclass items in my previous snippets.

To answer some of the comments so far:

  1. Why define focus? - I used the list and the variable focus as a way of getting the tiddler title when the default: current tiddler was used, otherwise the tiddler title appeared to be [all[current]]
  2. Why use the temp? I copy the content the field in question to a temp tiddler so that I can allow the user to edit it, but retain the option to discard their changes
  3. Why use the state tiddler? The state tiddler is used to instigate the “edit mode”, I tried just use the fact that a temp-tiddler existed, but every time I started editing the temp-tiddler I immediately broke the reveal match condition.

I found that I needed to use the div class=“paragraph-wrapper” in order to get the hover button to work, but that means this procedure always starts on a new line and can’t be used with inline text - not a big problem but not always desirable.

This is what I finished up with:

\procedure 
     edit-field(
          tiddler:[all[current]],
          field:"summary"
)
<$list filter=<<tiddler>> variable="focus">
<div class="paragraph-wrapper">
<$let
	temp-tiddler={{{ [[$:/edit-field/temp]] [<focus>] [<field>] :and[join[/]] }}}
	state-tiddler={{{ [[$:/edit-field/state]] [<focus>] [<field>] :and[join[/]] }}}
>
<$reveal type="nomatch" state=<<state-tiddler>> text="yes" tag="div" class=float-left>
     <$button set=<<state-tiddler>> setTo="yes" tooltip="Edit Field" class=tc-btn-invisible> 
     <$action-setfield $tiddler=<<temp-tiddler>> $value={{{ [<focus>get<field>] }}} /> 
           {{$:/core/images/edit-button}}
     </$button>
</$reveal>
<$reveal type="nomatch" state=<<state-tiddler>> text="yes">
     <$view tiddler=<<focus>> field=<<field>> />
</$reveal>
<$reveal type="match" state=<<state-tiddler>> text="yes" tag="div" class=float-left>
     <$button tooltip="Discard Changes" class=tc-btn-invisible>
            <$action-deletetiddler $tiddler=<<temp-tiddler>>/>
            <$action-deletetiddler $tiddler=<<state-tiddler>>/>
            {{$:/core/images/close-button}}
     </$button>
     <$button tooltip="Commit Changes" class=tc-btn-invisible>
            <$action-setfield $tiddler=<<focus>> $field=<<field>> $value={{{ [<temp-tiddler>get[text]] }}} /> 
            <$action-deletetiddler $tiddler=<<temp-tiddler>>/>
            <$action-deletetiddler $tiddler=<<state-tiddler>>/>
            {{$:/core/images/done-button}}
     </$button>
</$reveal>
<$reveal type="match" state=<<state-tiddler>> text="yes">
   <div class="paragraph-body"> 
         <$edit-text tiddler=<<temp-tiddler>> focus tag=textarea class=full-width/>
   </div>
</$reveal>
</$let>
</div>
</$list>
<style>
   .full-width { width: 100%; height: auto; }
   .paragraph-wrapper { position: relative; }
   .paragraph-wrapper .float-left {
       position: absolute;
       left: -24px;
       top: 0;
       display: flex;
       flex-direction: column;
       opacity: 0;
       transition: opacity 400ms;
       font-size: 20px;
   }
   .paragraph-wrapper:hover .float-left { opacity: 1; }
</style>
\end edit-field

''This is the summary field:'' <<edit-field>>type or paste code here

JSON version attached

edit-field.json (2.4 KB)