Button not executing actions from child widget, in code

99 views
Skip to first unread message

David Someone

unread,
Aug 19, 2016, 9:30:21 AM8/19/16
to TiddlyWikiDev
Hi all, could I kindly ask for some help in adding fieldmangler / action-sendmessage as a child widget to buttons?

I've made a copy of buttons.js and I'm trying to put action-sendmessage in the body of the code as a child widget. It's not firing the message up the chain, however other child widgets are working.

Example:

If I do this in a tiddler:

<$button>
    Button!
    <$fieldmangler>
        <$action-sendmessage $message="tm-add-tag" $param="MyTag"/>
        <$text text="chTxt"/>
    </$fieldmangler>
</$button>

I get a button which includes the child text widget:

[Button!chTxt]

and clicking it does add "MyTag" to the tiddler.

However, if I modify the button widget itself (button2.js) to add these same child widgets:

    var chWidgetsArray = [];
       chWidgetsArray
.push(
         
{ type: "fieldmangler",
            children
: [
               
{ type: "action-sendmessage", message: "tm-add-tag", param: "MyTag" },
               
{ type: "text", text: "chTxt" }
           
]
         
}
       
);
   
this.makeChildWidgets(chWidgetsArray);

I get the right button (including the child text widget):

[button!chTxt]

but clicking it does not add the tag.

I've futzed around by changing message: to $message:, by putting either variation in an attributes: element, no luck.

I've even commented out the line in buttons2.js that says "We don't propagate messages etc."

Any ideas what I'm doing wrong?

Many thanks!
David.

PMario

unread,
Aug 19, 2016, 11:04:50 AM8/19/16
to TiddlyWikiDev
Hi David,

If you could describe, what you want to achieve, I think we can provide a solution without modifying the button.js file.

Skimming your code I'm pretty sure, this may work out of the box.

-m

David Someone

unread,
Aug 19, 2016, 11:21:14 AM8/19/16
to TiddlyWikiDev
Thanks Mario,

I'm trying to create a widget that looks at any tiddlers tagged by a tag, and creates buttons with the names of those tiddlers. Clicking one of the buttons clears all tags matching those button names, and sets the tag of the particular button clicked.

  • - Call a widget with a parameter "sourceTag" to create a row of buttons which:
    • Get list of Tiddlers tagged by sourceTag (say, "Colours")
      • Finds Tiddlers called "Red", "Blue", "Green"
    • Create buttons labelled "Red", "Blue", "Green"
    • Clicking "Red" clears tag "Red", "Blue", "Green" and sets tag "Red"

So... I'm thinking the easiest way is just to recreate the button.js widget into what I need. Would love to find an easier way, though!

Having said that... Really what I'm trying to learn is how to write a javascript widget that sends messages up the chain to the tiddler the widget is sitting on, and I'm stuck on doing this with a button widget! Child widgets made in button.js do work (i.e. text), but not action-sendmessage!

Thanks,
David.

PMario

unread,
Aug 19, 2016, 5:10:13 PM8/19/16
to TiddlyWikiDev
Hi David,

It was a bit more magic needed, than I thought. but here it is:

Create a tidder eg: tagButtons-marco and tag it $:/tags/Macro

\define tagWithSpaces()[[$(myTag)$]]

\define tagButtons(tag)
<$list filter="[tag[$tag$]] -[has[draft.of]]" variable="myTag">
<$button>
<$action-listops $tiddler="$:/temp/myTags" $field:list $subfilter="[tag[$tag$]] -[has[draft.of]]"/>
<$action-listops $tiddler=<<currentTiddler>> $tags="+[remove{$:/temp/myTags!!list}append<tagWithSpaces>]" />
<<myTag>></$button>
</$list>
\end


usage:

<<tagButtons tag:color>>

have fun!
mario

PMario

unread,
Aug 19, 2016, 6:06:47 PM8/19/16
to TiddlyWikiDev
On Friday, August 19, 2016 at 5:21:14 PM UTC+2, David Someone wrote:
Having said that... Really what I'm trying to learn is how to write a javascript widget that sends messages up the chain to the tiddler the widget is sitting on, and I'm stuck on doing this with a button widget! Child widgets made in button.js do work (i.e. text), but not action-sendmessage!

Hi,

Again - If you describe, what you want to achieve, we could probably tell you the easiest way. ..

If you just want to learn, who TW works, imo the best way is, to debug the existing and working code. Once you understand how the mechanism works, you can start to modify stuff. Browser debugging tools [F12] are your friends here. Set breakpoints and single step through the code. ... That's how I learn new code. It may seem cumbersome, but it sometimes is enlightening. ...

If I look at your code, I have the feeling, that you lost your way. ... That's not who the stuff is supposed to work. That's why it causes problems. ... Just my guess. No offence intended.

-mario

David Someone

unread,
Aug 19, 2016, 6:51:31 PM8/19/16
to TiddlyWikiDev
Wow, thank you! I have to say, I'm impressed but confused more! Looking at that, I think I'm scared off TW5 even more... Totally cryptic compared to the JavaScript modules! ;-)

Now I have more to learn. :-)

David Someone

unread,
Aug 19, 2016, 6:55:54 PM8/19/16
to TiddlyWikiDev
So, really the point is to do a widget in code so that I have the flexibility of doing whatever JavaScript goodness I need as I develop it. Makes it more flexible for me than doing it in TWMacro code.

Still don't see how to trigger the send message up the chain, and as for me losing my way - you assume I knew my way in the first place!! :-)

Thanks again for the help. Really interesting...

David Someone

unread,
Aug 19, 2016, 8:56:02 PM8/19/16
to TiddlyWikiDev
I think I've figured out what it does...


// Make a list of Tiddlers tagged with "myTag", which aren't drafts

<$list filter="[tag[$tag$]] -[has[draft.of]]" variable="myTag">

 
// Output the list as button widgets
 
<$button class="button off">
   
// When the button is clicked, make a temp Tiddler with these tags in a field called "list"

   
<$action-listops $tiddler="$:/temp/myTags" $field:list $subfilter="[tag[$tag$]] -[has[draft.of]]"/>

   
// Take the current Tiddler (where this is called from) and tag it
with a list with the tags from the list in the temp Tiddler removed, and
 tagWithSpaces
(which is myTag) appended (not sure here?)

   
<$action-listops $tiddler=<<currentTiddler>> $tags="+[remove{$:/temp/myTags!!list}append<tagWithSpaces>]" />
   
<<myTag>>
 
</$button>
</
$list>
\end

Next step is to reset the "class" of the buttons to "off", then set the "class" of the button I've clicked to "on". This is why I'm thinking it would have been easier to write a widget in JavaScript than to write a macro in TW. Again, totally impressed, but equally stumped! At least I'm learning!!

PMario

unread,
Aug 19, 2016, 9:00:29 PM8/19/16
to TiddlyWikiDev
On Saturday, August 20, 2016 at 12:51:31 AM UTC+2, David Someone wrote:
Wow, thank you! I have to say, I'm impressed but confused more! Looking at that, I think I'm scared off TW5 even more...

I didn't want to scare you :)
The whole TW UI is made with wikitext. So everything, that you see and use, in TW, is made using wikitext functions, without additional js involved.
 
Totally cryptic compared to the JavaScript modules! ;-)

Not really. Actually relatively straight forward, because it always follows the same rules. ... And I didn't add any explanations.
The filters can get tricky. That's true and the listops functions are very, very specialized utilities to effectively deal with lists.

You don't have to forget, that I created this globally usable macro, with all the functionality you described in just 9 lines of wikitext code.

That's a fraction of lines that are needed to do the same things in js, even if you use the existing core code. ...

Now I have more to learn. :-)

Yea. TW wikitext is very powerful. And yes there is a learning curve if you want to customize TW itself.

... Just saw your post ... I'll add some more info.

-m

PMario

unread,
Aug 19, 2016, 9:23:09 PM8/19/16
to tiddly...@googlegroups.com

On Saturday, August 20, 2016 at 2:56:02 AM UTC+2, David Someone wrote:
I think I've figured out what it does...

\o/

// define a text substitution macro that allows us to handle tiddler names, that contain spaces.
// this one took me an hour to get it right :/

\define tagWithSpaces()[[$(myTag)$]]

// define the macro named: tagButtons, that gets a "tag" parameter
\define tagButtons(tag)

// Make a list of Tiddlers tagged with $tag$, which aren't drafts
// $tag$ is replaced with: color   see usage: <<tagButtons tag:color>>
//   see: http://tiddlywiki.com/#Macro%20Definitions%20in%20WikiText
// myTag is a loop variable where the list widget stores at actual tiddler name,
//   that is tagged: color see: http://tiddlywiki.com/#ListWidget


<$list filter="[tag[$tag$]] -[has[draft.of]]" variable="myTag">

  // Output the list as button widgets
  // there are some core classes, that you can use to hide buttons eg: tc-button-invisible

  <$button class="tc-button-invisible">


  // When the button is clicked, make a temp Tiddler with these tags in a field called "list"
  // ... absolutley right :)


  <$action-listops $tiddler="$:/temp/myTags" $field:list $subfilter="[tag[$tag$]] -[has[draft.of]]"/>

  // Take the current Tiddler (where this is called from) and tag it
  // with a list with the tags from the list in the temp Tiddler removed, and
  // tagWithSpaces (which is myTag) appended.
  // .... right. first remove eg: red, green, blue  ... then append the one, that was clicked.


  <$action-listops $tiddler=<<currentTiddler>> $tags="+[remove{$:/temp/myTags!!list}append<tagWithSpaces>]" />

  <<myTag>>
  </$button>

</$list>
\end

--------------

\define tagWithSpaces()[[$(myTag)$]]   if you try this

\define tagWithSpaces() $(myTag)$

and use a tiddler name "with spaces" tagged: color and you apply the tag, you'll see what's going wrong. it adds 2 tags "with" and "spaces" instead of "with spaces" ...


Next step is to reset the "class" of the buttons to "off", then set the "class" of the button I've clicked to "on".

See tc-xxxx classes in tw help and my code above. 
Adding style sheets is easy. Just create a tiddler eg: myStyles and tag it: $:/tags/Stylesheet
It needs to contain valid CSS code.


This is why I'm thinking it would have been easier to write a widget in JavaScript than to write a macro in TW.

No. No way.
 
Again, totally impressed, but equally stumped! At least I'm learning!!

Glad you take the time :)

-mario

David Someone

unread,
Aug 19, 2016, 9:41:13 PM8/19/16
to TiddlyWikiDev


On Friday, 19 August 2016 21:23:09 UTC-4, PMario wrote:


Next step is to reset the "class" of the buttons to "off", then set the "class" of the button I've clicked to "on".

See tc-xxxx classes in tw help and my code above. 
Adding style sheets is easy. Just create a tiddler eg: myStyles and tag it: $:/tags/Stylesheet
It needs to contain valid CSS code.

I've got stylesheets OK in general, what I'm thinking to do is apply a style to the button as it's created. I'm guessing I need to do something like this:

(forgive me, I'm going to mix languages here! I'm sure it'll make your eyes hurt!)

if (iClickedThisButton) {
\define $buttonClass$ = "on"
} else {
\define $buttonClass$ = "off
}
<$button class="
$buttonClass$">
  <$list .... all your other code >

Again, bear with me... learning as I go - and you're being an excellent tutor! I see what you mean by it being much, much shorter in TW Macros than in JS... once you "get it"!

Matabele

unread,
Aug 20, 2016, 1:26:58 AM8/20/16
to TiddlyWikiDev
Hi David

I wrote a number of widgets some time ago (before the introduction of Action Widgets) where parameters are passed up through a stack of widgets (stacked button widgets.) These may be found here.

This mechanism is, however, cumbersome when compared with the Action Widget mechanism -- I would stick with Action Widgets for this functionality.

You might also be interested in the example at the bottom of the 'keep Operator examples' tiddler, from the ActionListops widget wiki. This makes use of a couple of additional filters, the cycle[] filter and the keep[] filter which aren't yet in the core. In this case, the buttons search the listed tiddlers for the first item matching a list of items in a reference list, then exchange this item for the next item in the reference list i.e. if the tiddler were 'tagged' with "red green blue", and the reference list were "red yellow orange green blue", the first click of the button would leave the tiddler tagged with "yellow", the second click with "orange", and the third click with "green". This is similar to your intended functionality.

regards

David Someone

unread,
Aug 20, 2016, 2:20:37 AM8/20/16
to TiddlyWikiDev

Thanks for those links. I think I'm starting to get the hang of it... For instance, I think I created a similar Select macro:

\define tagWithSpaces()[[$(myTag)$]]
\define toggleTagSelect(tag)
 
<$select>

   
<$list filter="[tag[$tag$]] -[has[draft.of]]" variable="myTag">

     
<$action-listops $tiddler="$:/temp/myTags" $field:list $subfilter="[tag[$tag$]] -[has[draft.of]]" />

     
<$action-listops $tiddler=<<currentTiddler>> $tags="+[remove{$:/temp/myTags!!list}append<tagWithSpaces>]" />

     
<option value={{<<myTag>>!!text}}><<myTag>></option>
   
</$list>
  </
$select>
\end

although the value isn't working, as TW5 doesn't seem to interpret variables in transclusions, so:

value={{<<myTag>>!!text}}

comes out blank when I inspect the option element. I've tried a bunch of variations.

This is all very strange and interesting!

David Someone

unread,
Aug 20, 2016, 1:28:00 PM8/20/16
to TiddlyWikiDev
WOW! I answered my own question! I'm seriously impressed.

To recap:
PMario made a macro (thank you!) that reads titles tagged with myTag, and makes buttons with those titles.
The buttons, when clicked, clear all tags from the Tiddler it's on which match the titles we've read, and sets a tag matching the button we've clicked.

The next question was how to set the class of the button, depending on whether the Tiddler has a tag matching that button.

And the answer is... add a $set around the button, where the filter checks the current Tiddler for a tag matching the myTag variable.

\define toggleTagButtons(tag)


<$list filter="[tag[$tag$]] -[has[draft.of]]" variable="myTag">

 
<$set name="buttonClass" filter="[is[current]tag<myTag>]" value="button on" emptyValue="button off">
 
<$button class=<<buttonClass>>>

   
<$action-listops $tiddler="$:/temp/myTags" $field:list $subfilter="[tag[$tag$]] -[has[draft.of]]"/>
   
<$action-listops $tiddler=<<currentTiddler>> $tags="+[remove{$:/temp/myTags!!list}append<tagWithSpaces>]" />

   
<<myTag>>
 
</$button>
  </
$set>
</$list>
\end

Figuring out the logic of it was really, really hard because I didn't know what macros were available. I was hoping for an $if macro, which doesn't exist, but because I'm looking for an on/off result, I was able to figure out (also hard!) the right filter for the $set macro - thanks to some other posts here from the great Eric Shulman.

Whee! This is fun. My brain hurts.

PMario

unread,
Aug 20, 2016, 2:49:06 PM8/20/16
to TiddlyWikiDev
On Saturday, August 20, 2016 at 7:28:00 PM UTC+2, David Someone wrote:
WOW! I answered my own question! I'm seriously impressed.

Cool! - me too.
Welcome to the club

have fun!
mario

David Someone

unread,
Aug 22, 2016, 10:16:32 AM8/22/16
to TiddlyWikiDev
You have no idea the smile I had when you said "Welcome to the club"! =-) Thanks for that!

I found a small bug I can't figure out... If I use the macro multiple times on the same widget, (say, for making 2 rows of buttons from 2 different tags), they both use the same temporary state Tiddler. Thus, clicking on one row resets the other:


<$action-listops $tiddler="$:/temp/myTags" $field:list $subfilter="[tag[$tag$]] -[has[draft.of]]"/>
<$action-listops $tiddler=<
<currentTiddler>> $tags="+[remove{$:/temp/myTags!!list}append<tagWithSpaces>]" />

I haven't been able to figure out how to make each *instance* of this macro use it's own temp Tiddler. I would have thought it has something to do with the <<qualify>> macro, but can't get my head around it.

Any hints there? I'd imagine this should be a common pattern, using a "random" tiddler name for each state instance (although I can't find a "random" macro either!)

Thanks PMario et. al.!


David Someone

unread,
Aug 22, 2016, 12:11:35 PM8/22/16
to TiddlyWikiDev
Woo! Got this too..

The temporary Tiddler that saves the state of the tags needs to be named for the Tiddler and the tag in question, like this:

$:/temp/<currentTiddler>/$tag$

This way each instance on each Tiddler with this widget on it has it's own temporary Tiddler named for the Tiddler/instance.

Tobias Beer

unread,
Aug 28, 2016, 8:40:22 AM8/28/16
to TiddlyWikiDev
Hi David,

Just saw PMarios first response and thought, mhhh... let me test this, and I also figured this as well:

The temporary Tiddler that saves the state of the tags needs to be named for the Tiddler and the tag in question, like this:

$:/temp/<currentTiddler>/$tag$

This way each instance on each Tiddler with this widget on it has it's own temporary Tiddler named for the Tiddler/instance.

While you reported having tackled all this, let me still post my result,
which anyone can test it right away on TiddlyWiki.com.
(It helps if examples work that way ;-)

To toggle tags at a tiddler, the below:
  • specifies a filter parameter rather than just a tag (more flexible)
  • sets a button highlight, when already assigned as a tag
  • uses states properly
  • works as a toggle, removing the tag when clicked again
\define state() $:/temp/toggle-tags/$(currentTiddler)$/$(item)$
\define replace() +[remove{$(state)$!!list}append<title>]
\define remove() +[remove{$(state)$!!list}]
\define title() [[$(item)$]]

\define toggle-tags(filter)
<$list filter="$filter$ -[has[draft.of]]" variable="item">
<$set name="cls" filter="[is[current]tag<item>]" value="button on" emptyValue="button off">
<$button class=<<cls>>>
<$action-listops $tiddler=<<state>> $field:list $subfilter="$filter$ -[has[draft.of]]"/>
<$list filter="[<cls>] -[[button on]]" variable="none">
<$action-listops $tags=<<replace>> />
</
$list>
<$list filter="[<cls>] -[[button off]]" variable="none">
<$action-listops $tags=<<remove>> />
</
$list>
<$text text=<<item>>/>
</
$button>
</$set>
</
$list>
\end

!! Example

; filters
: <<toggle-tags filter:"[tag[Filter Operators]removesuffix[ Operator]]">>

; macros
: <<toggle-tags filter:"[tag[Macros]removesuffix[ Macro]]">>

<style>
.button.on {color:deeppink;}
</style>



Best wishes,

Tobias.

Reply all
Reply to author
Forward
0 new messages