Closing <$set>, nested SetWidgets

I don’t understand why SetWidget elements, <$set>…</$set>, are always closed in the examples, with nested child widgets.

What if I want to set a variable to be generally available?

This structure suggests to me that everything is rendered inside a nested widget hierarchy. If this is so, what is the effect of nested <$set> widgets? Is a variable defined at a grandparent level available to a grandchild widget?

More importantly, if I need two variables, do I have to nest two <$set> widget declarations inside one another?

<$set name="back" filter="[<sequence_index>subtract[1]]">
    <$set name="forward" filter="[<sequence_index>add[1]]">
        <div class="navigation_button">
          <$button>Back (#<<back>>)
            <$action-setfield sequence_index=<<back>>/>
            <$action-setfield squence_index=<<forward>>/>
          </$button>
      </$set>  
  </$set>

Could we just do, for instance:

<$set name="back" filter="[<sequence_index>subtract[1]]" \>
<$set name="forward" filter="[<sequence_index>add[1]]" \>

I’d appreciate any help understanding this.

Procedures (or macros) are intended for that.

The variable is available in all “descendant” levels, unless on some of these levels it is reassigned a different value.

If you need some of the “advanced” features of set widget, like assigning a filter result, yes. I’d like to be proven wrong though.
If you want to assign a static value or the first result of a filtered transclusion, you can use the let widget to assign multiple variables using one widget, so in your example

<$let back={{{ [<sequence_index>subtract[1]] }}} forward={{{ [<sequence_index>add[1]] }}} >
...
</$let>

It is possible (and often used in the core) not to close widget tags on the end of the tiddler. It is probably no the best practice though. So you could have the “opening” set/let widget tags in the beginning and skip the </$set> or </$let> in the end of the tiddler.

So in your example code, you could skip both </$set> in the end. Btw. there are typos, it should be </$set>, not </set>.

Thanks. I fixed the typos.

This gives me further related thoughts…

Widgets obviously don’t have a one-to-one correspondence with DOM elements. Do they have the same syntactic rules as HTML?

I have a feeling widgets don’t behave much like DOM elements, despite looking like them in wiki code. They’re also not objects in the sense of object-oriented JavaScript, or even just pragmatically. Practically speaking, tiddlers are the basic object motif in the design pattern for tiddler code.

So, how are widgets related to their child content? It’s not like widgets nested inside widgets are methods of a class, and they’re not necessarily going to become children elements in the DOM.

I am beginning to guess that hierarchy actually doesn’t matter in tiddler code, even though it appears that way at first.

Because of best practice. You could open several set-widgets at the top of a tiddler but it will break as soon as you add different elements. eg:

<$set name=test value=1>
<div>
<$set name=xyz value=2>
<<test>> -- <<xyz>>
<!-- </$set> -- is missing here so there will be a rendering problem with the </div> -->
</div>

Which renders as follows. You can see the closing div causes a problem since the closing set is missing.


If you want to define something “tiddler globally” you can use \procedure eg:

\procedure test() 1
\procedure xyz() 2

<<test>> -- <<xyz>>

Internally the procedure definition is converted to a “set-widget”, which is valid for the whole tiddler.


If you need a “tiddler global filter” you can use a \function.

\function back() [<sequence_index>subtract[1]]
\function forward() [<sequence_index>add[1]]

your code

hope that helps
-m

1 Like

They have a very similar semantic as HTML code. They are more like custom-elements in HTML
Widgets correspond with the DOM, but 1 widgets can create several DOM nodes.

Most widgets do create corresponding DOM nodes. eg:

<$link to=HelloThere>Hello There</$link>

creates an A element with an internal link.

Widgets are the basic building blocks in TW wikitext. The whole TW UI are widgets.
Internally (js-code) TW has a “widget” base class and every other widget inherits the basic elements from there. Every widget then defines its own functions. So there is a “class” system.

They have a “parent” - “child” relationship and “variables” defined by the parent are visible to the children.
Variable have a scope from widget-start to widget-end. So variables defined in nested widgets will not bleed out to the parents.

<$let variable="outer" test="Test">

variable: ''<<variable>>'' -- ''<<test>>''

  <$let variable=inner>
    variable in nested let-widget: ''<<variable>>'' -- ''<<test>>''

  </$let>

variable: ''<<variable>>'' -- ''<<test>>''
</$let>

Nested widgets do become children of widgets. But not every widget does create DOM nodes. Eg: The let-widget from the example above does not create dom nodes. Also transclusions do not create dom nodes.

That’s a wrong assumption. Hierarchy absolutely matters. See my “nested let” example.

hope that helps
-mario

Yes, I have observed this unclosed . I did not know what was causing it. I’m very surprised, because I thought widgets were being parsed differently from HTML tags in the tiddler.

Thank you for your thorough explanation!

I figured out I can closed the variable declaration widgets in the same tag, like XHTML,
, etc., but then that seemed to throw off the scope of the <$set> declarations.

I think action widgets evidently should be closed in the same declaration tag. I wonder if you could semantically use:

<$action-setfield></$action-setfield>

But of course most use cases of the action widgets won’t need any content inside of the widget, because the widget’s attributes are referenced for the designated operations.

See: https://tiddlywiki.com/#ActionSetFieldWidget

Content and Attributes

The action-setfield widget is invisible. Any content within it is ignored.

Hi @strivenword variables defined by widgets like the <$set> widget are only available within the children of that widget.

As you have correctly inferred, TiddlyWiki renders the page by building a hierarchical tree of widgets. Some of those widgets may generate DOM nodes, but not all of them do.

Global variables are defined by placing their definition in a tiddler tagged $:/tags/Global.

The default page template uses the <$importvariables> widget to bring the globals into the main widget tree.

That is correct. Nested <$set> widgets work by cumulatively building up the defined variables.

Yes.

Broadly, yes, but TiddlyWiki is a good deal simpler than the HTML parser, and isn’t as “sloppy” in terms of recovering from errors.

Widgets share the syntax of HTML elements intentionally. Internally, HTML elements in wikitext are handled as instances of the “<$element>” widget. So widgets are the fundamental component in TiddlyWiki rendering. As you note, tiddlers are the fundamental component in the TiddlyWiki data model.

Widgets are rendered to a tree. Internally, widgets are actually responsible for rendering their children.

I would not agree with that characterisation. TiddlyWiki uses strict hierarchical trees to represent parse trees and the widget tree.

I don’t think the core does feature any examples of unclosed tags. I would consider it a bug if any were found.

2 Likes

I apologize for the misinformation then. I have found where I took this idea from. $:/core/macros/tag contains a comment Closing tags omitted for brevity. In older version it did indeed contain unclosed tags (and I had tinkered with it back then), but it doesn’t anymore.

In a quick search I did find an example of an unclosed tag though: $:/core/templates/tiddlywiki5.html has an unclosed <$set> widget.

No worries at all, that’s very helpful. I’ve made a PR to fix the missing closing tag, and remove the obsolete comment:

1 Like