Skip to content

Notifications

For the most part, the Chart API is such that the hosting page calls methods on the chart in order to get desired results.

In some cases though the hosting page could benefit from knowing about things going on without its direct involvement.

For example, some modifications to the chart’s definition are not caused by the hosting page’s actions, but internally by chart (great example is annotation getting deleted using keyboard).

In order to keep things simple, we’re using exactly the same notification method externally (to the chart) as we are internally. A great pattern for loosely coupled messaging is publish/subscribe where objects that need to communicate about events happening either publish a message describing the event or subscribe to receiving notifications about given events. This pattern allows publisher and subscriber to be completely independent and not know about each other at all.

The exact library chart uses for this purpose is this one. You can find more details about the pattern in general and its advantages there.

So how do you go about receiving notifications from chart? You subscribe to events, the following of which are meaningful outside of the chart (though by peeking at the source you can find more events and are free to subscribe to them it’s not useful to do so at the moment):

  1. Annotation changed (CH_ANNOTATIONCHANGED), you get instance of the chart (mostly so that you can do something with its model, for example save it)
  2. Zoom changed (CH_ZOOMCHANGED), you get instance of the chart (mostly so that you can do something with its model, for example save it); the notification is debounced, so you’ll get one only 3 seconds after the sequence of zooms has ceased (typically zoom events come in bursts, chart waits until this stops plus 3 seconds)
  3. Annotation needs context menu (CH_ANNNEEDSCONTEXTMENU), you get { id: \<annId>, uid: \<annUid>, traits: \<currentTraits>, chart: \<chartObject>, offset: { x: \<offsetX>, y: \<offsetY>}, extras: \<currentExtras>, points: \<arrayOfCurrentPoints> }; please note that extras contains all non-editable data saved with the annotation (chart does not interpret this data) and that points contains all annotations points
  4. Pane heights changed (CH_PANEHEIGHTSCHANGED), you get instance of the chart (same reason like [1] above)
  5. Panes have been reordered (CH_PANESREORDERED), you get instance of the chart (same reason like [1] above)
  6. Chart “needs” (you’re not required to do so) a context menu (CH_CHTNEEDSCONTEXTMENU), you get chart and offset exactly like in [2]
  7. There is no data for the main symbol (CH_NODATAFORMAINSYMBOL), you get the instance of the chart; this is fired for cases where it’s normal not to have any data for the symbol (most Funds have no intraday data, for example), there is no error; note that we are also showing the No data to display so if that’s the only thing you need to do, no need to subscribe to this event
  8. User clicked on a “card” plot (CH_CARDPLOTCLICKED), fired when user clicks on a title/name of a plot in a secondary tooltip (AKA card) in the chart pane, you get instance of the chart and a property plot which contains all the info you need to send back when calling Plot Update API, for details see Chart API which is the main use case for this event (showing the UI allowing the user to customize the plot clicked on)
  9. User removed a plot by clicking on a “card” (CH_CARDPLOTREMOVED), see previous point for details and note that payload is the same
  10. User clicked somewhere on the chart (CH_ANNPOINTPICKED) after a (synthetic) PickPoint tool/annotation has been active; you get chart (like above) and point: { time, price } where time is the timestamp and price is the price of the point clicked at (always of the main plot)
  11. Chart has been recreated (CH_RECREATED) or removed from the page (CH_SHUTDOWN); as payload you get chart’s id as that may be the only thing left (when chart is removed)
  12. All data has been “projected”/drawn (CH_ALL_DATA_PROJECTED) which fires at the point where we’re received and recalculated all the data and painted it at least once; this event is useful if you need to know when the whole chart has been “fully loaded” whether to export the image of the chart or do any other kind of post-processing.
  13. If you set tooltip.mode to external in the default template (or change this using the API) the chart will stop showing tooltips of any kind but will notify you about the changes to tooltip through CH_TOOLTIPCHANGED; as payload you’ll get a very complete JSON object with all the information needed, shape of which is below; the only interesting property is lastBarRefresh which is set to true and provides only main plot’s last bar value in case there’s no tooltip logically active and you still want to refresh the display (typically outside of the bounds of the chart) with the latest values
{
"date": "<date>",
"time": "<time>",
"lastBarRefresh": true,
"chartId": "<id>",
"panes": [
{
"bounds": {
"..."
},
"plots": [
{
"isMain": true,
"typeName": "Symbol",
"name": "ESM18",
"description": "<description_only_for_studies>",
"values": [
{
"abbreviation": "C",
"shortName": "Close",
"id": "Close",
"name": "Close",
"longName": "Close",
"color": "#00B04B",
"value": "2,719.25"
},
"..."
]
}
]
},
"..."
]
}

offset is relative to the div hosting the chart and is taken directly from the DOM mouse context menu event (and helps you position the context menu).

We don’t want to hard code the names of the ‘topics’ to subscribe to so chart exposes the same taxonomy of names it uses internally to you (names are static members of the Topics class). The names to use when subscribing are in the parenthesis above.

A complete example is provided in the chart.html file in the root of this repo, but for completeness here’s an extract (a source code example is worth a thousand words 😉); this code usually goes along with the rest of the initialization code.

function applyAnnotationChange(chart, action) {
chart.change(action);
}
function onAnnotationContextMenu(msg, data) {
setTimeout(applyAnnotationChange, 150, data.chart, {
id: "Annotation",
context: { id: "Duplicate", uid: data.uid },
});
setTimeout(applyAnnotationChange, 250, data.chart, {
id: "Annotation",
context: { id: "Update", uid: data.uid, traits: { fill: { color: "#f00" } } },
});
}
// somewhere in the initialization phase
var brwc = Barchart.RealtimeWidgets.Charts;
brwc.PubSub.subscribe(brwc.Topics.CH_ANNOTATIONCHANGED, onAnnotationChanged);
brwc.PubSub.subscribe(brwc.Topics.CH_ANNNEEDSCONTEXTMENU, onAnnotationContextMenu);

The example is contrived, but does demonstrate what you can do with the API. If it’s not obvious, when annotation changes we simply dump the current model (whole model) which we receive in the notification. When chart needs context menu, we simulate delayed action from a user (using setTimeout) and then first duplicate, then update visual traits of the annotation in question.