Charting plugins - ApexCharts and Chart.js

My previous post to the Groups forum was copied here as I intended, but I now realise it’s read only. So…

A few days ago I released a plugin to allow the ApexCharts charting library to be used with TiddlyWiki.

Since then, I’ve been able to adapt that code to make a second plugin that allows use of the Chart.js library.

My TiddlyWiki where you can get these plugins is at:

http://www.scss.com.au/family/andrew/tiddlywiki/

Both plugins work the same way - chart definitions are created in JSON format. You will need to read the respective chart library documentation. I have supplied some procedures to create chart data from dictionary tiddlers. I have extensive examples for the ApexCharts plugin. Chart.js examples are limited.

I hope these are useful.

Cheers,
Andrew

12 Likes

I’m looking forward to checking these out in detail. My brief look is encouraging in that they look easier to use than eCharts.

@andrewg_oz are you situated in Australia or are you on the “Yellow brick road”? :nerd_face:

Thanks for sharing this it looks like it is well intergrated and powerful. Of course it will demand an investment in time to become fully conversent with this appropach to charting. It however seems to be a worthy investment in time given its intergration.

  • To make this useful to a broader audience, if you were to develop a set of useful charts “out of the box” it would be of value to users even, if they do not compose their own charts.
    • These can also be crowd/comminity sourced and shared.
  • Brainstorm for canned charts
    • Filter based charts with named fields to draw values from
    • Frequency charts based on date fields
    • Charts composed for common requirements like project management, timelines, trends and money.
  • I raise this because I see value in getting the community to commit to a specific charting solution and building a use case, that encorages wide spread adoption.

An observation;

In the plugin tab filtersequence I notice at the bottom the javascript equivalent reads as [{"a":1},{"a":2},{"a":3}] which is very close to the $params value in the ParametersWidget which would be {".0.a":1},{".1.a":2},{".2.a":3}.

This may allow you to prepare a procedure, widget and transclusion that can pass parameters directly on to the apex charts which could be a useful feature for quick and easy Apex charts of simple parameter/value pairs.

Thanks for your contribution @andrewg_oz

Re “oz” comment - I’m from the “land down under” :upside_down_face: (clue is in the domain name)

Minor (or major, depending on your point of view) update to my Chart.js plugin - I’ve added support for the “datalabels” plugin, which I’ve made a TW plugin (a “plugin plugin”?)

I have no idea how other people might use these plugins - they’ve primarily been written because I needed a plugin! I’ve already written procedures to convert TiddlyWiki dictionary tiddlers to the respective plugins data structures, so creating a simple chart could be as easy as setting up that dictionary tiddler, then putting a suitable procedure call into the widget body - basically a three-line chart. See:

http://www.scss.com.au/family/andrew/tiddlywiki/#ApexChart%20-%20Pie%20-%20Simple
http://www.scss.com.au/family/andrew/tiddlywiki/#ChartJS%20Pie%20Test

which could trivially (delete the single line of extra customisation each has) be such examples.

I’m planning to leave that as-is for the moment and see what people think, rather than do a whole pile of “maybe” procedures.

Regarding the parameters widget - I’m not sure what you mean. The filter sequence is about implementing chart library callbacks that need to return data to the library almost always for single data points at a time, not generating bulk data for charting. I may have gone overboard a little with the syntax, since I don’t think I’ve needed to return an object - it’s been almost always strings and maybe one time an array. If the example you gave is what the $params value to the widget looks like (the TiddlyWiki documentation doesn’t show any examples), then it appears to be the same syntax as what I’m using, which is nice.

Bulk data is best done using a procedure to generate a compatible JSON data structure, like my dictionary tiddler procedures. I suspect, however, that once you get beyond dictionary tiddler data sources, it rapidly becomes more complicated than generic utility procedures can cope with.

I’ll give it a bit of time and see what happens.

Andrew

1 Like

Thank you for sharing! This looks fantastic. I will be needing charts in a few months and it’s great to have a simple tool to use for them.

@andrewg_oz thought you may be in OZ, although I did not notice the .au, I am in Sydney.

I am yet to work through the operation of your plugins, although thanks for sharing.

All I am suggesting here is if we could pass a javaScript “object” to it that can be generated from parameters; it may be possible to “request” simple charts using a just some parameters. Even simpler.

However my initial look into the <$apexchart widget suggests I may be able to construct a method myself.

For example a procedure <<pie "dark energy":68 "dark-matter":27 matter:"5">> would present a sumple pie chart, with the need for a separate data tiddler.

Code snipit

\procedure pie()
<$parameters $params=params>
calling `<$apexchart` with <<params>>
</$parameters>
\end pie

<<pie dark-energy:68, dark-matter:27, matter:5>>

result, just illustrating.

calling <$apexchart with {"dark-energy":"68,","dark-matter":"27,","matter":"5"}
  • The above method would work immediately if a variable can be given as the data input to $apexchart
  • A common alternative in tiddlywiki is $names and $values parameters containing filters as in SetMultipleVariables widget.
  • Whatever you wish, of course. This was not intended to be a detailed request, just I noticed it may be a simple step to invoke your plugin(s) a different way (or may not be), allowing easy use and further integration.

Hopeful the above illustrates what I mean. I am not a JavaScript programmer (yet), but I am well versed in TiddlyWiki, as you say I think it may be;

I agree with the followig;

I can help with the following if and when you come to it.

But for now “I gotta go learn some things” :nerd_face:

Thanks again.

Hi @andrewg_oz

I appreciate your work on TiddlyWiki charting and I’ve been exploring your plugins. Nice job, thanks for sharing! :slightly_smiling_face:

May I suggest some improvements in your examples?

In your example tiddlers, you often use this code pattern for value series:

<$list counter="is" filter="[[$:/plugins/tiddlywiki-scss-com-au/chartjs]plugintiddlers[]]">
<$text text={{{ [<is-first>match[no]then[,]] }}}/>
<$text text={{{ [<currentTiddler>]+[get[text]length[]] }}}/>
</$list>

and then this for labels:

<$list counter="is" filter="[[$:/plugins/tiddlywiki-scss-com-au/chartjs]plugintiddlers[]]">
<$text text={{{ [<is-first>match[no]then[,]] }}}/>
"<$text text={{{ [<currentTiddler>]+[removeprefix[$:/plugins/tiddlywiki-scss-com-au/chartjs/]] }}}/>"
</$list>

Notice here the subtle variation with leading and trailing " around the second $text widget.
Example code borrowed from this tiddler.

There are repeated lines of code in both code blocks:

  1. The whole $list widget, which selects the source tiddlers holding the data
  2. The first $text widget, which adds a , separator between values

The second point can be simplified by adding a +[join[,]] filter step to the values filter, which also obsoletes the counter="is" attribute of the $list widget, like this:

<$list filter="[[$:/plugins/tiddlywiki-scss-com-au/chartjs]plugintiddlers[]]">
<$text text={{{ [<currentTiddler>]+[get[text]length[]]+[join[,]] }}}/>
</$list>

This works for values, but not for labels because of the surrounding ", which can be replaced by a +[addprefix["]addsuffix["]] filter run:

<$list filter="[[$:/plugins/tiddlywiki-scss-com-au/chartjs]plugintiddlers[]]">
<$text text={{{ [<currentTiddler>]+[removeprefix[$:/plugins/tiddlywiki-scss-com-au/chartjs/]]+[addprefix["]addsuffix["]]+[join[,]] }}}/>
</$list>

These code blocs can be further simplified by merging $list filter inside $text filtered transclusion:

<$text text={{{ [[$:/plugins/tiddlywiki-scss-com-au/chartjs]plugintiddlers[]]+[get[text]length[]]+[join[,]] }}}/>
...
<$text text={{{ [[$:/plugins/tiddlywiki-scss-com-au/chartjs]plugintiddlers[]]+[removeprefix[$:/plugins/tiddlywiki-scss-com-au/chartjs/]]+[addprefix["]addsuffix["]]+[join[,]] }}}/>

This is more compact, but also less readable… Enter subfilter:

\define chartTiddlers() [[$:/plugins/tiddlywiki-scss-com-au/chartjs]plugintiddlers[]]
\define chartValues() [get[text]length[]]
\define chartLabels() [removeprefix[$:/plugins/tiddlywiki-scss-com-au/chartjs/]] +[addprefix["]addsuffix["]]

...

<$text text={{{ [subfilter<chartTiddlers>] +[subfilter<chartValues>] +[join[,]] }}} />

...

<$text text={{{ [subfilter<chartTiddlers>] +[subfilter<chartLabels>] +[join[,]] }}} />

The whole code before:

<$chartjs type=pie title="Plugin Composition" width=530px>{
"options":{"plugins":{"legend":{"position":"right"}}},
"data":{"datasets":[{"data":[
<$list counter="is" filter="[[$:/plugins/tiddlywiki-scss-com-au/chartjs]plugintiddlers[]]">
<$text text={{{ [<is-first>match[no]then[,]] }}}/>
<$text text={{{ [<currentTiddler>]+[get[text]length[]] }}}/>
</$list>
]}],"labels":[
<$list counter="is" filter="[[$:/plugins/tiddlywiki-scss-com-au/chartjs]plugintiddlers[]]">
<$text text={{{ [<is-first>match[no]then[,]] }}}/>
"<$text text={{{ [<currentTiddler>]+[removeprefix[$:/plugins/tiddlywiki-scss-com-au/chartjs/]] }}}/>"
</$list>]}}</$chartjs>

and after:

\define chartTiddlers() [[$:/plugins/tiddlywiki-scss-com-au/chartjs]plugintiddlers[]]
\define chartValues() [get[text]length[]]
\define chartLabels() [removeprefix[$:/plugins/tiddlywiki-scss-com-au/chartjs/]] +[addprefix["]addsuffix["]]

<$chartjs type=pie title="Plugin Composition" width=530px>{
"options":{"plugins":{"legend":{"position":"right"}}},
"data":{"datasets":[{"data":[

<$text text={{{ [subfilter<chartTiddlers>] +[subfilter<chartValues>] +[join[,]] }}} />

]}],"labels":[

<$text text={{{ [subfilter<chartTiddlers>] +[subfilter<chartLabels>] +[join[,]] }}} />

]}}</$chartjs>

I hope you liked this little journey, and thanks again for your plugins :slightly_smiling_face:

Fred

Hi Fred,

Thanks for your comments.

TiddlyWiki filters, macros, procedures, functions and similar things are fundamental to TiddlyWiki and I struggle with how they all work together.

For some things I started with “+[join[,]]” but later found it unusable in some cases and ended up with the list/counter pattern. I have clearly re-used that pattern where join would have worked and been the better option. It’s good to be reminded of these things!

I’ve uploaded a new wiki. The composition charts are simpler now. A couple of other charts got similar treatment.

Andrew

2 Likes

@andrewg_oz I was trying to use the apex charts with a Habit-tracker. Habit tracker is saving data in a json tiddler like this. Is it possible to retrieve data from such a json tiddler for creating charts.

Nice plugins, so you created two plugins, supporting ApexChart and Chart.js, and I can see on you website you also created another version of echarts plugin, I think this has been done by ECharts - Bringing amazing visualization tools to TiddlyWiki!.

But seems charts.js is much smaller, which can be a nice replacement for smaller wikis.

But what into my concern is that we might need to install multiple chart library if other plugins like Habit tracker in TW directly depend on them.

A better approach will be like Fishing, flashcard in TiddlyWiki, add new features to help remember tiddler more efficiently in release v1.2.0, it make its analysts feature be a sub-plugin, so user can choose install a version with the chat library they already installed. (Can have 2 analysts sub-plugin, depends on charts.js and echarts)

The short answer is yes.

The longer answer is that all the chart libraries tend to use JavaScript timestamps (that is, milliseconds since 1-Jan-1970) and that the dates in the JSON will need to be parsed into something that can be ultimately converted into a timestamp. It will also make the date sortable - something I assume you’d want to do and something that can’t be done in the string form the dates are in now.

I’ve done some testing and found that the date in your JSON cannot be parsed by the TiddlyTools parsedate function. In other words, “21/9/2023” won’t work, but “9/21/2023”, “21-Sep-2023”, “20230921”, “2023/9/23” will. Other formats might work too, like an ISO8601 or similar. My first preference for writing dates is “21-Sep-2023” since it is clear which part is a year, month, or day. My second preference is ISO8601, so “20230921”

Once date parsing is resolved you’ll be able to use TW widgets to construct a suitable JSON that can be charted.

I’ve just updated my wiki with the following:

http://www.scss.com.au/family/andrew/tiddlywiki/#Test%20Json:[[Test%20Json]]%20[[Data%20from%20Json]]

I don’t know what sort of chart you’re after, but the general idea is this:

  1. Change your JSON date format to something parsable. Ideally it would be directly sortable without parsing, like a TW timestamp.
  2. Use TW widgets to construct a chartable JSON.

The “Data from Json” tiddler converts the JSON dates into a sortable form (“YYYY0MM0DD”) then sorts them. Then it sets a “key” variable that returns that sortable date back to the original format in the JSON to allow lookups to work. This could be avoided if the dates were already stored in the JSON in a sortable form.

Then an example table is output using the key for looking up data in the JSON, the JavaScript (not TW) timestamp for charting, and the looked-up value (also for charting?)

I’ve tried to keep my chart plugins as “thin” as possible, since I don’t know how other people will try to use them.

I really don’t see much alternative for plugins that want to support charting other than to provide (or allow for) chart-specific plugins.

There are a couple of things I could think of that I could change.

One would be to support a “json” parameter that would provide an additional way to provide chart data to the widget. Another way would be to support a parameter “dict” (for “dictionary tiddler”) that would make use of the dictionary-tiddler-macros I’ve written for my ApexCharts and Chart.js widgets.

I’m not sure of the best way to proceed, so I’m just seeing what ideas people may have before doing anything more.

Thank you @andrewg_oz for taking a look at this issue. As per your advice, I changed the date format in the json tiddler into a parsable format.

But the Value column is remaining empty

image

Any idea whats going wrong here ?

I think the most widely recognized, easily parseable format is the ISO-8601 version with the hyphens, 2023-09-21, which conveniently extends to including times as well. And it’s easy to convert to TW date format. In JS:

const d = new Date()
console.log(d.toISOString())                     //=> 2023-09-21T16:35:48.771Z
console.log(d.toISOString().slice(0, 10))        //=> 2023-09-21
console.log(d.toISOString().replace(/\D/g, ''))  //=> 20230921163612589

Go jogging_data.json (338 Bytes)

2 Likes

When you’re creating the key variable the format string has to match the format in the JSON. In your case, it should be “YYYY-MM-DD”.

@andrewg_oz … There seems to be a problem with Andrew's TiddlyWiki — Plugins and MacrosI does not work with FireFox

Is there a GitHub repo to report bugs?

@andrewg_oz I got it working as you suggested.
Do you know of any charts which can display yes or no data like this from json tiddlers

Timeline chart may fit your needs.

1 Like

@john.edw_gmail.com How to modify the code of this timeline chart tiddler to get the data from this tiddler.

@pmario No repo, sorry. The problem was in the demo data - apparently Firefox doesn’t parse “1793-11-31” as a date (only 30 days in November!)