What is the best way to take the fields of a tiddler and display them as a table?

So out of curiousity, without the use of plugins or anything, what would be the easiest way to take the fields of a tiddler, ie a field of “device-model” and a field value of “generic laptop” and show this as a table like?

| device-model | generic laptop |

Just genuinely curious how more senior users would handle this. I’m currently tackling this and a little bit stumped.

So far, what I have right now is:

<$list filter="[<currentTiddler>fields[]] -created -tags -title -text -draft.of -draft.title -modified">

| <<currentTiddler>> |  |

</$list>

It’s just the field values part that has me a bit… stumped :sweat_smile:

Unfortunately, you’ll need a $list widget to do this, and lists don’t really play well with wikitext tables. Here’s what I’d do, conceptually…

<table>
<$list filter="[<currentTiddler>fields[]] -text +[sort[title]]" variable="field">
	<tr>
		<td><<field>></td>
		<td><$text text={{{ [<currentTiddler>get<field>] }}} /></td>
	</tr>
</$list>
</table>

You may notice this is pretty similar to the table produced by the Fields tab of a tiddler’s Info section (accessed via the “More actions” button in a default setup.) The Fields tab is using $:/core/ui/TiddlerInfo/Fields as its template, which is itself transcluding $:/core/ui/TiddlerFields, which uses the following code to generate its fields table:

<table class="tc-view-field-table">
<tbody>
<$list filter="[all[current]fields[]sort[title]] -text" template="$:/core/ui/TiddlerFieldTemplate" variable="listItem"/>
</tbody>
</table>

You can see the approach is pretty similar; the table rows have just been moved into $:/core/ui/TiddlerFieldTemplate, which is used as the template for each list item. So my final answer:

  • If you want to display a particular set or subset of fields, copy the text from $:/core/ui/TiddlerFields (reproduced above) and replace the list filter with your own filter or list of field names.
  • If you want a table just like the one in the Info tab, just use {{||$:/core/ui/TiddlerFields}}.

And just for fun, this thread includes some somewhat hacky but functional ways to have your $list and your wikitext table too: Construct table while iterating using the $list widget :slight_smile:

2 Likes

I didn’t even think about that but yes that would of been perfect to use! I was looking at the tags manager and was just turning the wikitext into an html table.

Justin, The first place to look for code to do things in tiddlywiki is in tiddlywiki.

  1. On any tiddler select the info button and fields.
  2. Visit control panel > Advanced > Tiddler fields

With the “link to tabs plugin”, installed you can open the appropriate tiddlers that code this;

  1. $:/core/ui/TiddlerInfo/Fields
    uses <$transclude tiddler="$:/core/ui/TiddlerFields"/> which contains;
\whitespace trim
<table class="tc-view-field-table">
<tbody>
<$list filter="[all[current]fields[]sort[title]] -text" template="$:/core/ui/TiddlerFieldTemplate" variable="listItem"/>
</tbody>
</table>

and uses
$:/core/ui/TiddlerFieldTemplate to list each

\whitespace trim
<tr class="tc-view-field">
<td class="tc-view-field-name">
<$text text=<<listItem>>/>
</td>
<td class="tc-view-field-value">
<$view field=<<listItem>>/>
</td>
</tr>
  1. $:/core/ui/ControlPanel/TiddlerFields uses;
\define lingo-base() $:/language/ControlPanel/

<<lingo TiddlerFields/Hint>>

{{$:/snippets/allfields}}

$:/snippets/allfields contains

\define renderfield(title)
<tr class="tc-view-field"><td class="tc-view-field-name">''<$text text=<<__title__>>/>'':</td><td class="tc-view-field-value">//{{$:/language/Docs/Fields/$title$}}//</td></tr>
\end
\whitespace trim
<table class="tc-view-field-table">
<tbody>
<$list filter="[fields[]sort[title]]" variable="listItem">
<$macrocall $name="renderfield" title=<<listItem>>/>
</$list>
</tbody>
</table>

But also see the FieldsWidget and its include exclude options. Perhaps reuse $:/core/ui/TiddlerFieldTemplate as the template.

That’s a pretty good idea, but one of the things I need is a specific exclusion of fields, and to take the fields of the tiddler the table is being made in, rather than showing the fields of other tiddlers.

This is something I am still trying to figure out :thinking:

I have a viewTemplate that transcludes tiddlers based on how they are tagged, and shows them as tabs to the tiddler they are tagged with.

Because of this, using the tables above works as intended, but when those tiddlers are transcluded as tabs, the table populates with the tiddler fields of the tagging tiddler.

I’m currently expirimenting with targetTiddler and storyTiddler inplace of currentTiddler.
I may end up replacing with {TheTiddlersTitle} but wanted to create something a bit more modular

I didn’t even know we had a $fields widget! Looking at it, though, it does seem to be used primarily for the saving mechanism, and it has some special rules for its template:
image

So <$fields template="$:/core/ui/TiddlerFieldTemplate" /> won’t work - you’d have to try something like this <$fields template="| $name$ | $value$ |" />… and then you run into the usual iterating-over-wikitext-table issues.

image

1 Like

If I’m following correctly, you should be able to replace <currentTiddler> with <currentTab> to get the results you’re looking for. Alternately, wrap the whole thing with a $tiddler widget:

<$tiddler tiddler=<<currentTab>>>
Your table goes here
</$tiddler>

This will set <<currentTiddler>> to the value of <<currentTab>> if it’s being used as a tab template. Outside a tabs macro, <<currentTab>> is undefined, so the value of <<currentTiddler>> doesn’t change. This lets you transclude the same code into other contexts if you need to.

1 Like

Yep! that works as intended!

So! For final results:

<$tiddler tiddler=<<currentTab>>>
	<table class="tc-max-width">
		<$list filter="
			[<currentTiddler>fields[]] -text -tags -created -modified -title -draft.of -draft.title -caption +[sort[title]]" variable="fields">
				<tbody>
					<tr>
						<th><<fields>></th>
						<td><$text text={{{ [<currentTiddler>get<fields>] }}} /></td>
					</tr>
			</tbody>
		</$list>
	</table>
</$tiddler>

I thought I would also share how I exclude some fields.

\whitespace trim
\function standard.fields() created tags title text draft.of draft.title modified
<table class="tc-view-field-table">
<tbody>
<$list filter="[all[current]fields[]sort[title]!standard.fields[]]" template="$:/core/ui/TiddlerFieldTemplate" variable="listItem"/>
</tbody>
</table>
  • This just shows how I like to define a function containing standard.fields, and in this case not containing !standard.fields[]
  • The use of currentTab as @etardiff points out it needed when ever using tabs. If you look at sidebar tabs they don’t really have a current tiddler, only currentTab.
1 Like

I tried giving this a shot because it looked pretty useful, but I’m not sure I understand how to use it correctly.

Here’s what I tried to do:

\function tagged.function() {{{ [enlist{!!tags}] -[[Section Tiddlers]] }}}

<<list-links "[tagged.function[]tag[Task Tiddlers]]">>

Using above in a tiddler as a template, and then in tiddlers I want to use it as with {{||TemplateTiddlersTitleHere}}

However, it doesn’t seem to work :thinking:

You are using a “filtered transclusion” (tripple curly braces) in the function.

  • This is not valid in a function they can only contain filter syntax
  • You could use this [enlist{!!tags}] -[[Section Tiddlers]] but this will only remove from the list the title Section Tiddlers.
  • If the “tagged. Function” was working your use of list-links should work
    • However keep in mind the list-links macro is a limited shorthand version of using the $list widget, which has more features.

This should work although may not be exactly what you are after,

  • It is ofent easier for us to give you code rather than repair code, so ask for what you want to do
\function ignore.tags() notthistag [[not this one eaither]]
\function tagged.function() {{{ [enlist{!!tags}!ignore.tags[]] }}}

<<list-links "[tagged.function[]tag[Task Tiddlers]]">>

Unfortunately that did not 🫤

I have to head to sleep for now, but I will revisit this tomorrow to give more context.

Tomorrow, remember if you do this it is easier to help you,

Noted, I’ll try to give a bit more detail to what I’m trying to do.

Alright, back in business!

So, picking up from where I left off, I’ve tried using:

\function ignore.tags() notthistag [[not this one eaither]]
\function tagged.function() {{{ [enlist{!!tags}!ignore.tags[]] }}}

<<list-links "[tagged.function[]tag[Task Tiddlers]]">>

Which did not function when placed into a tiddler that would be transcluded with:

title: Temporary TiddlyWiki

{{||Task Tiddlers Section Template}}

And, just to include the detail here for reference, this tiddler will also be shown as a tab in my custom viewTemplate, which uses the following code to turn specifically tagged tiddlers into tabs in the tiddlers they are tagged with:

title: $:/user/ui/ViewTemplate/tabbed-tiddler/all-tagged

<<list-links "[!has[draft.of]<currentTiddler>tagging[]!sort[]]" field:"title" emptyMessage:"@@.tc-muted //Nothing here.// @@">>

My goal is for a tab to generate whenever a tiddler is tagged with another tiddler, as well as the tag “Section Tiddler” and in that tiddler, it should list tiddlers tagged as “Task Tiddlers” to list all the tasks that are needed for that topic (the tiddler the tab tiddler is tagged with.)

So! I made it work, albeit I did not find a use for the \function mechanism, but it was fun to expiriment with and helped me figure out what I needed to do.

The final code that works for me is:

<$list filter="[<currentTiddler>tagging[]] -[{$:/user/ui/ViewTemplate/tabbed-tiddler/section-tag}]" variable="tags"> <!-- This lists the tags of tiddlers tagged by this tiddler. -->

<<list-links "[<tags>tag[Task Tiddlers]]">>

</$list>
title: $:/user/ui/ViewTemplate/tabbed-tiddler/section-tag
text:" Section Tiddlers "

I’m glad you found your solution! I’m going to include the response I was writing below, just in case you do find yourself needing functions in the future.


As Tony noted, you can’t use {{{ }}} in a function definition. The \function pragma expects a filter (vs. something like $let, which expects a value, or something that evaluates to a value—a variable like <<currentTiddler>> or a filtered transclusion like {{{ [tag[HelloThere]] }}}, where the first result is assigned as the value).

Here’s an example, which finds all tags that are themselves tagged with TableOfContents, then excludes anything listed in ignore.tags:

\function ignore.tags() HelloThere Learning

<<list-links "[tags[]tag[TableOfContents]!ignore.tags[]]">>

Or a slightly more complicated example:

\function starts.with(prefix) [prefix<prefix>]

<<list-links "[tags[]tag[TableOfContents]!starts.with[H]]">>
  • tag is one of the most efficient filter operators, so if you’re using it, it’s good to include it as close to the start of the filter as possible, so later steps have fewer titles to consider.
  • As you can see in the second example in particular, the function is being applied like a subfilter.

And a sidenote in case you haven’t caught it already:

You have some broken syntax here. You should never see a sequence like ]< (or ]{ or ]{, or any similar pattern) inside a filter; if you do, it’s a good sign there’s something wrong! When you string two bracketed values together without any intervening filter operators, the filter will throw out everything that came before the second value, which becomes its new and only input.

So in this case, !has[draft.of] will be entirely ignored. I’d rewrite the filter this way instead:

[<currentTiddler>tagging[]!has[draft.of]!sort[]]
2 Likes

Ahh ok- so it is literally writing new custom filter steps to use, TBH I was thinking about it the same as how I think of definitions or procedures, which admittedly I have only a brief understanding of.

However- I’m not sure I understand the difference between when you point out using {{{ }}} and {{{ [tag[Hello There]] }}}. Is it because the result of that filtered transclusion is only a single result?

Ah, that’s good to know, any optimizations I can make will help in the long run.

Oh, wow ok that I was not aware of, but I’ll keep that in mind moving forward when writing filters, because that could cause me quite a bit of issues later down the road.

Right. \function has some similarities to both \define and $let, but it’s not exactly like either. Here’s a quick comparison:

  • I tend to write all my functions with.periods and avoid using periods in macro names or variables, just so it’s very clear which is which, but I used periods in all the names here for consistency.

What \define does: defines a string of text as a variable. The text will get literally pasted (not evaluated in advance) wherever you use the variable, and then rendered at the same time as the rest of the tiddler. (You can see the difference in Definition and Definition #2.)

  • defines a variable
  • can’t be used as a custom filter operator; see the results when we try to use it in the $list.

What $let does: defines a string of text as a variable. If this text can be evaluated (like a transclusion), it will be evaluated at the point where $let is used, and the final value will be substituted wherever you use the variable.

  • defines a variable
    • If you use a filtered transclusion (as I did with test.let), the value of <<test.let>> is set to the first result returned by the filter.
    • If you want to preserve the full list, you’d have to use test.let={{{ [tag[HelloThere]] +[format:titlelist[]join[ ]] }}}, and then retrieve it with [enlist<test.let>]
  • can’t be used as a custom filter operator
  • is evaluated once; a string defined with \define is evaluated (if applicable; at the same time as its context) each time you use the variable. This means $let is sometimes more efficient, however…
  • everything inside $let will get rerendered every time the value of the variable changes

What \function does: defines a filter as a variable. The filter will get evaluated in context every time you use the function; the results differ depending on how you use it.

  • can be used as a variable; <<test.function>> = the first value of the filter. To keep the full list, you need to use format:titlelist[]join[ ] (just like using $let with filtered transclusion).
  • can be used as a filter operator if the name of the function contains at least one period.
    • functions like a subfilter/a standard filter operator, and can return more than one result
    • can also be called within a filter using the function operator: function<test.function> = test.function[]
    • can take one or more parameters (like \define and \procedure can), which will be substituted with the values you specify when you use the function. This is the trick I used with starts.with[H]:

You can name any number of space- or comma-separated parameters in the function definition and use them as variables in the function’s filter. When you call the function (as a variable or inside a filter), the values you specific will be substituted in the order they’re given and used to evaluate the function.

\function another.function(tag, prefix, suffix) [tag<tag>prefix<prefix>suffix<suffix>]

{{{ [another.function[tag],{!!prefix},<suffix>] }}}

is equivalent to

{{{ [tag[tag]prefix{!!prefix}suffix<suffix>] }}}
  • You can use $let or \define to define a static filter and then call it in a filter with subfilter<my-filter> or filter<my-filter>, but you can’t do the kind of variable substitution that you can with \function.

Re: macros vs. procedures…

Procedures are just the modern version of macros—and like macros, they don’t evaluate prior to substitution. The only major difference is the way they handle variables and text substitution:

Procedures can be more efficient since they’re not doing substitution. You still need \define for instances where you do need this kind of substitution, though, and I also like to use it to define static strings:

\define tag-list() tag1 tag2 tag3
\procedure tag-list() tag1 tag2 tag3

<!-- ^^ mean the same thing, but \define makes more sense to me when defining is really all I'm doing. -->
2 Likes

That’s a lot to process, but I can definitely get a better understanding of them from reading (and rereading… and re:rereading lol) this, so I appreciate it, and your help with the tabs and such :grinning_face_with_smiling_eyes:

1 Like