# Musson Industrial’s Embr-Charts Module

**URL:** <https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618>\
**Category:** 3rd Party Modules\
**Tags:** perspective\
**Created:** [June 10, 2024, 12:53pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618 "2024-06-10T12:53:46Z")\
**Posts on this page:** 20\
**Page:** 9

<div class="post-metadata">

**Author:** ![hw79](https://avatars.discourse-cdn.com/v4/letter/h/838e76/32.png) [@hw79](https://forum.inductiveautomation.com/u/hw79)\
**Post date:** [March 5, 2025, 2:18pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/161 "2025-03-05T14:18:56Z")

</div>

@bmusson How would I go about passing the context? of what was clicked on the chart to another perspective component?

(e.g. If I click on a slice of a pie chart I would get the `data.labels[x]` and and `data.datasets[0].data[x]` properties of what was clicked. In a standard component I would expect that to be in the event object from an onClick event but I'm guessing that's not the case here.)

What I'm trying to accomplish is to send that to a bar chart that breaks that slice 'category' down into sub-categories.

edit: maybe it's something to do with options.events?

edit2: I've managed to make a little progress and uncovered the pieces I need, but can't send them somewhere else in the component.

```python
// props.options.onClick
(event, elements, chart) => { 
		if (elements.length > 0) {
			const payload = {
				datasetIndex: elements[0].datasetIndex,
				index: elements[0].index
			};
			console.log('Sending payload:', payload);

			// this component.notify is the closest thing I could get to 
			// system.perspective.sendMessage() but I think it works in a 
			// different way because I keep getting an error:
			// level: WARNING
			// message: "\"ComponentStore\" cannot notify \"chartClick\" state change. No registered listeners or handler."

			perspective.context.component.notify('chartClick', payload);
		} else {
			console.log('No elements clicked');
		}
	}

```

closer...

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [March 6, 2025, 6:32pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/162 "2025-03-06T18:32:00Z")

</div>

~~It probably comes down to determining the 'correct' listener name?~~

 

~~Looking in the contexts, by adding a an action to the `onClick` mouse event in the designer, a new item is added to the `perspective.context.component.domEvents` node called `onClick`. That seems to be hard bound to whatever function is defined in the designer but there seems to be at least 1 layer of abstraction in there.~~

Completely misunderstood your goal here. I wouldn't be surprised at all if the entirety of the messaging system in perspective is entirely gateway side with none of the actual messaging occuring at the client level, which is where your javascript is running.

---

<div class="post-metadata">

**Author:** ![hw79](https://avatars.discourse-cdn.com/v4/letter/h/838e76/32.png) [@hw79](https://forum.inductiveautomation.com/u/hw79)\
**Post date:** [March 6, 2025, 7:15pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/163 "2025-03-06T19:15:42Z")

</div>

that's what I was afraid of. Until I can figure out how to get something from the chart events out to a session/page/view message handler, I'm afraid I'll have to use Apex charts for this one.

as a side note to your edit, listeners in the component don't seem to be the same as listeners in the project and seem to be isolated, or as you said client-side and the same goes for `perspective.context.component.notify()`

I appreciate your reply.

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [March 6, 2025, 7:32pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/164 "2025-03-06T19:32:50Z")

</div>

If you are using chartjs for the second chart, you might be able to define a custom event listener on that chart and pass it a CustomEvent via `dispatchEvent`.

You could use that to transfer the necessary data from the main chart to the secondary chart. It would also bypass the round trip through the gateway.

Basic flow would be to add an item to the secondary chart's `props.plugins` with a definition for `install` that defines and attaches the event listener to the chart canvas. Most basic listener would look for new data in the event and shove that data into the `data.datasets` of the chart.

In your main chart's `props.options.onClick` definition, you would create a new CustomEvent that would contain the data you want to send, and maybe some other info to drive what the chart should do, and then pass this CustomEvent to the eventListener on the secondary chart.

> **[EventTarget: addEventListener() method - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)**
>
> The addEventListener() method of the EventTarget interface
> sets up a function that will be called whenever the specified event is delivered to the target.

> **[CustomEvent - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent)**
>
> The CustomEvent interface can be used to attach custom data to an event generated by an application.

> **[EventTarget: dispatchEvent() method - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent)**
>
> The dispatchEvent() method of the EventTarget sends an Event to the object, (synchronously) invoking the affected
> event listeners in the appropriate order. The normal event processing
> rules (including the capturing and optional bubbling phase) also...

Including this which shows basic creation and attaching of eventListeners:

> [@\[BUG?\] Perspective Power Chart Bug - Embr-Chart as a Replacement?](https://forum.inductiveautomation.com/t/bug-perspective-power-chart-bug-embr-chart-as-a-replacement/102084/36):
>
> tada Yeah my first example didn't track the mouse very well. Here's a version that should be exactly 1-to-1 by using some Chart.js scale helpers (getPixelForValue and getValueForPixel). You'll have to add the mouse cursor stuff back in (or have Claude do it for you wink) (chart, event) =\> { if (event.event.type !== 'mousedown') return const { x: clickX, y: clickY } = event.event const scale = chart.scales.y if (clickX \> scale.right || clickX \< scale.left) return l…

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [March 6, 2025, 8:06pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/165 "2025-03-06T20:06:23Z")

</div>

> [@hw79](#):
>
> @bmusson How would I go about passing the context? of what was clicked on the chart to another perspective component?

Your only option currently is to write the payload to the property tree, then use an `onChange` script.

Like this:

```js
// props.options.onClick
(event, elements, chart) => { 
  if (elements.length > 0) {
    const payload = {
      datasetIndex: elements[0].datasetIndex,
      index: elements[0].index
    }
    
    // Ensure that you add a custom property on the chart component
    // named 'clickPayload'.
    this.store.custom.write('clickPayload', payload)

  } else {
    console.log('No elements clicked')
  }
}

```

> [@ryan.white](#):
>
> I wouldn't be surprised at if the entirety of the messaging system in perspective is entirely gateway side with none of the actual messaging occuring at the client level, which is where your javascript is running.

This.

It would be nice to fire messages from client side JavaScript through... let me mull it over.

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [March 6, 2025, 9:01pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/166 "2025-03-06T21:01:52Z")

</div>

> [@bmusson](#):
>
> JavaScript through... let me mull it over.

![messaginTs](https://us1.discourse-cdn.com/inductiveautomation/original/3X/b/9/b9b790fd1111d082460df0ddeb14bfdac0cda47e.png)

That was fast 😆. Guess I should hurry my attempt to implement DecimalFormat in js.

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [March 6, 2025, 9:06pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/167 "2025-03-06T21:06:16Z")

</div>

Sometime you get lucky, and things are easy 🙂

> **[Release @embr-modules/charts@2.1.0 · mussonindustrial/embr](https://github.com/mussonindustrial/embr/releases/tag/%40embr-modules%2Fcharts%402.1.0)**
>
> Patch Changes
> 
> Updated dependencies \[8921225\]
> 
> @embr-modules/charts-web@2.1.0

The `perspective` global object now includes a function for firing gateway-side messages.

```js
perspective.sendMessage(type: string, payload: JsObject, scope = 'page') => void

```

Your example can now become:

```js
(event, elements, chart) => { 
  if (elements.length > 0) {
    const payload = {
      datasetIndex: elements[0].datasetIndex,
      index: elements[0].index
    }

    perspective.sendMessage('chartClicked', payload, 'view')

  } else {
    console.log('No elements clicked')
  }
}

```

This is also available in Periscope.

---

<div class="post-metadata">

**Author:** ![csjohnson](https://avatars.discourse-cdn.com/v4/letter/c/5f8ce5/32.png) [@csjohnson](https://forum.inductiveautomation.com/u/csjohnson)\
**Post date:** [March 7, 2025, 3:10am UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/168 "2025-03-07T03:10:55Z")

</div>

Not sure if it helps you at all, but I have elements that when clicked on, it just writes the data I want to a custom property on the view. I also send the index and put a change event script on that property. I use the change event to open popups with the context data as params. Then reset the index.  
It’s probably the “poor-boy” way to do it, but it works.

---

<div class="post-metadata">

**Author:** ![hw79](https://avatars.discourse-cdn.com/v4/letter/h/838e76/32.png) [@hw79](https://forum.inductiveautomation.com/u/hw79)\
**Post date:** [March 7, 2025, 2:25pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/169 "2025-03-07T14:25:45Z")

</div>

I would have gone with `this.store.custom.write()` but now with the update I'm using the sendMessage() function and I've already set up the charts. I did have the Apex charts set up, but they are so slow compared to Embr.

![2025-03-07_08-24-49](https://us1.discourse-cdn.com/inductiveautomation/original/3X/e/4/e4cdab1d3a27ff68a80d9b1c0e9a3831e5ac0866.gif)

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [March 7, 2025, 2:52pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/170 "2025-03-07T14:52:20Z")

</div>

Awesome 😀

Also, you might find this plugin useful:

> **[GitHub - sgratzl/chartjs-plugin-hierarchical: Chart.js scale for hierarchical tree-like data...](https://github.com/sgratzl/chartjs-plugin-hierarchical)**
>
> Chart.js scale for hierarchical tree-like data structure

---

<div class="post-metadata">

**Author:** ![csjohnson](https://avatars.discourse-cdn.com/v4/letter/c/5f8ce5/32.png) [@csjohnson](https://forum.inductiveautomation.com/u/csjohnson)\
**Post date:** [March 7, 2025, 3:00pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/171 "2025-03-07T15:00:17Z")

</div>

Totally missed the `this.store.custom.write()` function. May have been in a newer version than what I have.  
I was doing it this way: (i think Ben helped me out with this)

```python
(event, elements, chart) => {
    if (elements.length !== 0) {
        const selected = elements.map((e) => ({
        datasetIndex: e.datasetIndex,
        index: e.index,
        }));
        const data = this.props.data.datasets[selected[0].datasetIndex]
        const selectedEvent = {
            'index': selected[0].datasetIndex,
            'data': data
        }

        const view = perspective.context.view;
        view.custom.write("selectedEvent", selectedEvent);
    }
}

```

---

<div class="post-metadata">

**Author:** ![hw79](https://avatars.discourse-cdn.com/v4/letter/h/838e76/32.png) [@hw79](https://forum.inductiveautomation.com/u/hw79)\
**Post date:** [March 7, 2025, 4:22pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/172 "2025-03-07T16:22:24Z")

</div>

is that included in your module? I could set `options.scales.x.type` to heirarchical and then bring in the data dictionary like the example?

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [March 7, 2025, 4:25pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/173 "2025-03-07T16:25:45Z")

</div>

Yup, exactly. If you run into issues just post them here.

---

<div class="post-metadata">

**Author:** ![hw79](https://avatars.discourse-cdn.com/v4/letter/h/838e76/32.png) [@hw79](https://forum.inductiveautomation.com/u/hw79)\
**Post date:** [March 7, 2025, 7:05pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/174 "2025-03-07T19:05:29Z")

</div>

just got a chance to play with this again, I'm getting this error:

```python
level: LEVEL_ERROR
message: "Error: \"hierarchical\" is not a registered scale."
line_number: 121
source: "https://...:8043/res/perspective/js/react-dom-18.2.0.js"

```

`props.data` JSON

```python
{
  "labels": [
    "A"
  ],
  "datasets": [
    {
      "data": [],
      "label": "Test",
      "tree": [
        1
      ]
    }
  ]
}

```

`props.options.scales` JSON

```python
{
  "x": {
    "type": "hierarchical"
  },
  "y": {
    "type": "linear"
  }
}

```

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [March 7, 2025, 7:12pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/175 "2025-03-07T19:12:55Z")

</div>

Looking at the source code it looks like that scale is added to the plugin registry but not specifically registered? Could just be a nuance with how external plugins are registered.

```typescript
import { HierarchicalScale } from 'chartjs-plugin-hierarchical'
Chart.registry.addElements(HierarchicalScale)

```

vs

```typescript
import { TimestackScale } from 'chartjs-scale-timestack'
Chart.register(TimestackScale)

```

Edit: After looking at the implementation instructions from the plugin source I think this is the cause. I've opened issue [#248](https://github.com/mussonindustrial/embr/issues/248) on the github for this.

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [March 7, 2025, 10:20pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/176 "2025-03-07T22:20:16Z")

</div>

> **[Release @embr-modules/charts@2.1.1 · mussonindustrial/embr](https://github.com/mussonindustrial/embr/releases/tag/%40embr-modules%2Fcharts%402.1.1)**
>
> Patch Changes
> 
> a44e437: (Chart.js, Fix) Adjust Chart.js property schema to allow for objects as labels. This is needed for chartjs-plugin-hierarchical.
> a44e437: (Chart.js, Fix) Properly register Hi...

> **Example View JSON**
>
> ```json
> {
> "custom": {},
> "params": {},
> "props": {},
> "root": {
> "children": [
> {
> "meta": {
> "name": "Chartjs"
> },
> "position": {
> "basis": "300px"
> },
> "props": {
> "data": {
> "datasets": [
> {
> "label": "Test",
> "tree": [
> 1,
> {
> "children": [
> 3,
> {
> "children": [
> 4.1,
> 4.2
> ],
> "value": 4
> },
> 5
> ],
> "value": 2
> },
> {
> "children": [
> 7,
> 8,
> 9,
> 10
> ],
> "value": 6
> },
> 11
> ]
> }
> ],
> "labels": [
> "A",
> {
> "children": [
> "B1.1",
> {
> "children": [
> "B1.2.1",
> "B1.2.2"
> ],
> "label": "B1.2"
> },
> "B1.3"
> ],
> "expand": false,
> "label": "B1"
> },
> {
> "children": [
> "C1.1",
> "C1.2",
> "C1.3",
> "C1.4"
> ],
> "label": "C1"
> },
> "D"
> ]
> },
> "options": {
> "animations": {
> "numbers": {
> "properties": [
> "borderWidth",
> "radius",
> "tension",
> "radius",
> "tension"
> ]
> }
> },
> "layout": {
> "autoPadding": false,
> "padding": {
> "bottom": 60
> }
> },
> "scales": {
> "x": {
> "type": "hierarchical"
> },
> "y": {
> "type": "linear"
> }
> }
> },
> "type": "bar"
> },
> "type": "embr.chart.chart-js"
> }
> ],
> "meta": {
> "name": "root"
> },
> "props": {
> "direction": "column"
> },
> "type": "ia.container.flex"
> }
> }
> 
> ```

---

<div class="post-metadata">

**Author:** ![hw79](https://avatars.discourse-cdn.com/v4/letter/h/838e76/32.png) [@hw79](https://forum.inductiveautomation.com/u/hw79)\
**Post date:** [March 8, 2025, 12:18am UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/177 "2025-03-08T00:18:35Z")

</div>

Thanks so much, I really appreciate your quick attention to this!

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [March 10, 2025, 6:43pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/178 "2025-03-10T18:43:30Z")

</div>

# A Question For The Class

For my own use, I'd like to include an ApexCharts component in Embr-Charts.

## The Good

It would have all the nice JavaScripting features from the Chart.js component (more powerful events/method access), as well as reduced overhead compared to the Kyvis-Labs version (their component does some property processing that I think is silly).

## The Bad

ApexCharts is bundled in a way that makes it impossible to have two different versions running simultaneously. Therefore you would not be able to have the Kyvis-Labs ApexCharts module installed alongside Embr-Charts.

The Embr-Charts version of the component would not be a "drop in replacement" for existing projects.

## Options

1. Include an ApexCharts component in Embr-Charts, and ignore the consequences.
2. Include an ApexCharts component in Embr-Charts, but do not register it if the Kyvis-Labs module is installed (Kyvis-Labs component gets priority).
3. Create a whole separate module for the Embr version of the component.
4. Include a fork of Kyvis-Labs component in Embr-Charts.
5. Try to become a regular contributor to the Kyvis-Labs module, in order to keep the ApexCharts dependencies in sync between both modules.

All options are annoying/bad in their own unique ways 🎉.

#3 seems like the simplest/best option.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [March 10, 2025, 6:53pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/179 "2025-03-10T18:53:32Z")

</div>

I recommend #5, if they'll take your pull requests, otherwise #4.

My Tag Report Utility project currently allows switching between IA's TS Chart and synchronized Apex charts, to compare performance. I intend to add Embr as an option, with switching between any of the three (if installed). Not being able to load Apex alongside Embr would break that.

If the modules clash, some developers would choose Apex, and others would choose Embr, and there would be **no migration path** between them if one wanted to switch for any reason. That would be a disaster, I think.

If you do #4, please make ensure your changes don't interfere with it being a drop-in replacement for the Kyvis Labs version.

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [March 10, 2025, 7:58pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/180 "2025-03-10T19:58:57Z")

</div>

> [@pturmel](#):
>
> If the modules clash, some developers would choose Apex, and others would choose Embr, and there would be **no migration path** between them if one wanted to switch for any reason. That would be a disaster, I think.

Yeah, I agree that needs to be avoided at all costs.

Ultimately if there's not a satisfactory solution that's good for the community as a whole, we will just keep it to ourselves 🤷‍♂️.

[Previous page](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618.md?page=8)

[Next page](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618.md?page=10)
