Musson Industrial’s Embr-Charts Module

Is there a documentation site I can visit to see what properties come with the tree chart type without installing the module? I looked on the Chart.js docs site, but didn't see any chart of that type in the core or base Chart.js library. Is this an implementation of a 3rd party Tree library (e.g. sgratzl's chartjs-chart-graph)?

Yup, that’s exactly what it is. It looks like it’s missing from the list of included plugins, I’ll make sure it gets added. I believe most of his plugins are included; if there’s something extra you’d like to see included let me know.

1 Like

Wow, that's awesome - I can't wait to give it a try! I have some ideas for the Tree chart type, so if I can get those implemented, I'll be testing using this module for a few production systems, to start at least.

1 Like

Just a heads up, the designer schema currently doesn’t have any support for plugins (my focus has been on the main Chart.js properties), but any contributions are welcome if you’re able/interested.

Version 1.3.4:

  • Updated Chart.js to version 4.4.4.
2 Likes

Maybe I am missing it in the other replies, but I have a non-issue I have run into.

I have specific colors for pie slices and I am using the backgroundColor property as an array. This works as expected in rendering, but the designer is quite unhappy with me for it. I imagine you may need to add array as an option in your component.props.json.

Or is there a better way for me color my pie slices?
image

Unfortunately, if I do that we’ll lose the color picker for the single backgroundColor option :slightly_frowning_face:. I don’t know of a way to get the color picker for the property as both a string and an array.

The current state of the designer component property validator is kinda lame, it’s definitely in a “minimum viable product” state. There’s many parts of the Chart.js schema that push the boundaries of what the designer property validation can handle.

2 Likes

Totally makes sense! Just passing along what I encountered. Overall still great to use. I make the designer props tree angry often so no worries here! Thanks for giving the community an awesome tool!

2 Likes

I really do appreciate it, and if you come across anything else weird throw it in this thread or on GitHub issues.

I figure that the designer validator will eventually be improved, and when it does it’ll be nice to have these issues documented.

Maybe sometime in 8.3 land the validator will be better. A guy can hope anyways!

would it be possible to release an example config for dendogram/forceDirectedGraph/tree/treemap ?

I can't seem to get these working properly
According to sgratzl/chartjs-chart-graph: Chart.js Graph-like Charts (tree, force directed) (github.com) I should be able to use the parent tag to configure how points are connected but I can't get it working:

I'd like to get to a point where I can show PLC connections to Ignition, show the Slave's below those PLC's and show inter PLC connectivity

Idealy I'd use the forceDirectedGraph and just define the connections and whether or not a node is online or not.

Yup. I’m out of office the rest of this week, but I’ll put something together next Monday/Tuesday.

I’m on mobile, so excuse the poor explanation lol.

The chart type in your screenshot is wrong; either at the top of the props (props.type) or in the dataset (props.data.datasets[i].type) set the type to either graph , forceDirectedGraph , dendrogram , or tree.

treemap is something entirely different, and the type: line configured in your dataset is overwriting the top level property. In your screenshot the chart is rendering as a line chart.

hmmm..
I'm matching those type's now to dendogram but that results in a singlepoint for the first dataset and nothing for the other.

Looking forward to your working example when you have time :smiley:


same with forceDirectedGraph:

Alright, good news and bad news.

Bad News :slightly_frowning_face:
There is a bug in the chartjs-chart-graph plugin. The chart only renders once, and any subsequent renders throw errors. I've submitted an issue to the library author, you can track it here. I believe it's a simple fix.

Good News :grin:

  1. The issue can be side-stepped by enabling the newly added redraw property. When redraw is enabled, the chart will be completely destroyed/recreated on each render. Bad for performance but useful for debugging.
  2. This issue pointed out some extra re-rendering that was happening, it's been solved with some memoization. There should be a performance boost here.
  3. The components for the tree chart weren't registered, so those were never working. TreeChart and TreeController are now registered correctly.

I will publish a new version once a fix is available.

5 Likes

No news on the chartjs-chart-graph plugin, and I don't have time to make the fix myself :frowning:.

But, here's a new Embr-Charts release that includes properly registered TreeChart components. If you use the new redraw property you can sidestep the issue in the chartjs-chart-graph plugin at the cost of performance/animations.

@embr-modules/charts@1.4.0

Minor Changes

  • 60d382e: (Web) Memoize property transforms.
  • 60d382e: Add optional updateMode and redraw component properties.

Patch Changes

  • 60d382e: (Web) Reorganize javascript exports.
  • 60d382e: (Web) Register TreeChart and TreeController controllers from chartjs-chart-graph.
    • @embr-modules/charts-web@1.4.0

Embr-Charts-1.4.0.modl

2 Likes

Any tips on options>plugins>crosshair? I get an error when changing to true.

java.util.concurrent.ExecutionException: java.lang.Exception: Unable to get the view config: View config is undefined.

Thank you for this module btw.

For sure.

  1. The crosshair plugin doesn’t like to be partially configured, and when it crashes, it crashes catastrophically. The best way I’ve found is to configure it as crosshair2, then once you get it set up change the key to crosshair.
  2. The crosshair plugin seems to have its own zoom implementation that fights with the normal zoom plugin. I think I remember needing to disable the base zoom plugin.

:tada:

1 Like

Thank you for building this module. I'm finding it very useful.

Is there some way to utilize the event handlers, such as plugins.zoom.zoom.onZoom/onPan? Or to access the min/max x-axis values being shown as the user pans/zooms?

I have a time series chart where I'd like to make API requests based on the timeframe being shown as the user zooms/pans, but I'm unable to find a way to achieve that. Even just a means of accessing the min/max values being shown in the x-axis would suffice, but the scale properties don't appear to get updated as the user pans/zooms.

Yup, you can do this by using the Scriptable Options feature.

I've intentionally not turned these into component events, because I don't want to limit the possibilities.

Here's an example that will write the min/mix X-axis values to a custom property on the component:

Example View JSON
{
  "custom": {},
  "params": {},
  "props": {},
  "root": {
    "children": [
      {
        "custom": {
          "xAxis": {
            "end": 63.29596760742342,
            "start": -16.704032392576583
          }
        },
        "meta": {
          "name": "Chartjs"
        },
        "position": {
          "height": 300,
          "width": 633,
          "x": 69,
          "y": 242
        },
        "props": {
          "data": {
            "datasets": [
              {
                "data": [
                  {
                    "x": 0,
                    "y": 20.790464349448122
                  },
                  {
                    "x": 1,
                    "y": 27.541905413990484
                  },
                  {
                    "x": 2,
                    "y": 9.044548907913263
                  },
                  {
                    "x": 3,
                    "y": 63.58544584440057
                  },
                  {
                    "x": 4,
                    "y": 40.57122042169531
                  },
                  {
                    "x": 5,
                    "y": 46.17945691520222
                  },
                  {
                    "x": 6,
                    "y": 5.272367698783542
                  },
                  {
                    "x": 7,
                    "y": 78.62029735550435
                  },
                  {
                    "x": 8,
                    "y": 1.384758302338518
                  },
                  {
                    "x": 9,
                    "y": 8.094105544748587
                  },
                  {
                    "x": 10,
                    "y": 93.29711393437752
                  },
                  {
                    "x": 11,
                    "y": 78.54999817500953
                  },
                  {
                    "x": 12,
                    "y": 30.022085778849018
                  },
                  {
                    "x": 13,
                    "y": 68.38927062938775
                  },
                  {
                    "x": 14,
                    "y": 93.84973459203651
                  },
                  {
                    "x": 15,
                    "y": 85.15266831524416
                  },
                  {
                    "x": 16,
                    "y": 3.3272574045278946
                  },
                  {
                    "x": 17,
                    "y": 3.4738230710669837
                  },
                  {
                    "x": 18,
                    "y": 76.78237778113568
                  },
                  {
                    "x": 19,
                    "y": 76.13468451530511
                  },
                  {
                    "x": 20,
                    "y": 88.63284915490885
                  },
                  {
                    "x": 21,
                    "y": 92.74877419440537
                  },
                  {
                    "x": 22,
                    "y": 94.12886888152015
                  },
                  {
                    "x": 23,
                    "y": 97.06398992204936
                  },
                  {
                    "x": 24,
                    "y": 46.71625167184086
                  },
                  {
                    "x": 25,
                    "y": 13.224799335767212
                  },
                  {
                    "x": 26,
                    "y": 35.052260048430696
                  },
                  {
                    "x": 27,
                    "y": 29.46821488097423
                  },
                  {
                    "x": 28,
                    "y": 90.27395392440542
                  },
                  {
                    "x": 29,
                    "y": 7.832816289114186
                  },
                  {
                    "x": 30,
                    "y": 15.799390058813623
                  },
                  {
                    "x": 31,
                    "y": 80.66585380750348
                  },
                  {
                    "x": 32,
                    "y": 4.629220116475031
                  },
                  {
                    "x": 33,
                    "y": 24.00958904699422
                  },
                  {
                    "x": 34,
                    "y": 8.133906226255029
                  },
                  {
                    "x": 35,
                    "y": 81.93116748921958
                  },
                  {
                    "x": 36,
                    "y": 82.82333811028309
                  },
                  {
                    "x": 37,
                    "y": 95.37635776015469
                  },
                  {
                    "x": 38,
                    "y": 74.00252644137744
                  },
                  {
                    "x": 39,
                    "y": 27.90694570663671
                  },
                  {
                    "x": 40,
                    "y": 30.569029278167793
                  },
                  {
                    "x": 41,
                    "y": 11.104072480586257
                  },
                  {
                    "x": 42,
                    "y": 68.15581447149303
                  },
                  {
                    "x": 43,
                    "y": 27.051454957054943
                  },
                  {
                    "x": 44,
                    "y": 48.477001171626725
                  },
                  {
                    "x": 45,
                    "y": 73.62917163080661
                  },
                  {
                    "x": 46,
                    "y": 7.528701180738118
                  },
                  {
                    "x": 47,
                    "y": 21.794180888547032
                  },
                  {
                    "x": 48,
                    "y": 17.0019331614802
                  },
                  {
                    "x": 49,
                    "y": 80.4420964468141
                  }
                ],
                "label": "Dataset 1"
              },
              {
                "data": [
                  {
                    "x": 25,
                    "y": 99.00283352088223
                  },
                  {
                    "x": 26,
                    "y": 36.743648170189644
                  },
                  {
                    "x": 27,
                    "y": 33.58269158236888
                  },
                  {
                    "x": 28,
                    "y": 70.52608530981715
                  },
                  {
                    "x": 29,
                    "y": 78.48664561425423
                  },
                  {
                    "x": 30,
                    "y": 99.80258881269653
                  },
                  {
                    "x": 31,
                    "y": 23.56577175483505
                  },
                  {
                    "x": 32,
                    "y": 27.6260054014127
                  },
                  {
                    "x": 33,
                    "y": 11.84780583914382
                  },
                  {
                    "x": 34,
                    "y": 78.95435294680595
                  },
                  {
                    "x": 35,
                    "y": 89.0983947949238
                  },
                  {
                    "x": 36,
                    "y": 87.63589420154018
                  },
                  {
                    "x": 37,
                    "y": 82.4714423791225
                  },
                  {
                    "x": 38,
                    "y": 46.29407127415893
                  },
                  {
                    "x": 39,
                    "y": 12.463880984334686
                  },
                  {
                    "x": 40,
                    "y": 91.82293604614682
                  },
                  {
                    "x": 41,
                    "y": 9.645637547490315
                  },
                  {
                    "x": 42,
                    "y": 23.87358763050218
                  },
                  {
                    "x": 43,
                    "y": 64.35308758482624
                  },
                  {
                    "x": 44,
                    "y": 61.30614039090162
                  },
                  {
                    "x": 45,
                    "y": 60.123113026673906
                  },
                  {
                    "x": 46,
                    "y": 11.262924017594045
                  },
                  {
                    "x": 47,
                    "y": 36.72684549715057
                  },
                  {
                    "x": 48,
                    "y": 19.144808134626057
                  },
                  {
                    "x": 49,
                    "y": 11.499648689135334
                  },
                  {
                    "x": 50,
                    "y": 48.862954644422615
                  },
                  {
                    "x": 51,
                    "y": 44.0188922198424
                  },
                  {
                    "x": 52,
                    "y": 1.4731641405698048
                  },
                  {
                    "x": 53,
                    "y": 76.62654204425371
                  },
                  {
                    "x": 54,
                    "y": 97.09498548555429
                  },
                  {
                    "x": 55,
                    "y": 68.88623969428103
                  },
                  {
                    "x": 56,
                    "y": 9.347347267187478
                  },
                  {
                    "x": 57,
                    "y": 27.59945299037486
                  },
                  {
                    "x": 58,
                    "y": 38.62680310622668
                  },
                  {
                    "x": 59,
                    "y": 64.48467246665813
                  },
                  {
                    "x": 60,
                    "y": 99.50148874944306
                  },
                  {
                    "x": 61,
                    "y": 59.35003045509352
                  },
                  {
                    "x": 62,
                    "y": 7.407803152895475
                  },
                  {
                    "x": 63,
                    "y": 62.077958133975066
                  },
                  {
                    "x": 64,
                    "y": 19.550924069869623
                  },
                  {
                    "x": 65,
                    "y": 59.290461562229346
                  },
                  {
                    "x": 66,
                    "y": 15.46272365498117
                  },
                  {
                    "x": 67,
                    "y": 27.411246578970218
                  },
                  {
                    "x": 68,
                    "y": 59.515203740704415
                  },
                  {
                    "x": 69,
                    "y": 76.39871363134311
                  },
                  {
                    "x": 70,
                    "y": 20.255435491968
                  },
                  {
                    "x": 71,
                    "y": 57.26165130029921
                  },
                  {
                    "x": 72,
                    "y": 73.41996375745644
                  },
                  {
                    "x": 73,
                    "y": 68.57751118650746
                  },
                  {
                    "x": 74,
                    "y": 25.40840081665815
                  }
                ],
                "label": "Dataset 2"
              }
            ]
          },
          "options": {
            "plugins": {
              "zoom": {
                "pan": {
                  "modifierKey": null,
                  "onPan": "(context) \u003d\u003e { \n\nconst xAxis \u003d context.chart.scales.x\nconst customProps \u003d self.store.custom\n\ncustomProps.write(\u0027xAxis.start\u0027, xAxis.start)\ncustomProps.write(\u0027xAxis.end\u0027, xAxis.end)\n}"
                },
                "zoom": {
                  "onZoom": "(context) \u003d\u003e { \n\nconst xAxis \u003d context.chart.scales.x\nconst customProps \u003d self.store.custom\n\ncustomProps.write(\u0027xAxis.start\u0027, xAxis.start)\ncustomProps.write(\u0027xAxis.end\u0027, xAxis.end)\n}"
                }
              }
            },
            "scales": {
              "x": {
                "type": "linear"
              },
              "y": {
                "type": "linear"
              }
            }
          }
        },
        "type": "embr.chart.chart-js"
      }
    ],
    "meta": {
      "name": "root"
    },
    "type": "ia.container.coord"
  }
}

The key part is this:

# options.plugsin.zoom.pan.onPan
# options.plugsin.zoom.zoom.onZoom

(context) => { 

  const xAxis = context.chart.scales.x
  const customProps = self.store.custom

  customProps.write('xAxis.start', xAxis.start)
  customProps.write('xAxis.end', xAxis.end)
}

You can always use onPanComplete or onZoomComplete if that's more useful for you.

I'd also recommend slapping in a console.log(context) or a console.log(self) to see what you're working with. You have access to every part of the chart through the context parameter, and self is the Perspective component itself.

2 Likes