Good ole shows: how can I avoid invoking run loop functions when connecting Ember.js and Highcharts? or, come chat about how to avoid run loop usage

:wave: folks!

Here I am searching the Internet for how to connect Ember.js and Highcharts. Again. It feels like watching a good television series. Again.

I’ll start by sharing a couple assumptions. I most appreciate you commenting if you spot erroneous ones!

  1. The Run Loop is bad and should be avoided in Ember.js applications
  2. I should be able to create a Glimmer component that integrates with Highcharts but does depend on run loop functions in the component’s backing class.

How do you integrate with Highcharts or 3rd party libraries that depend upon a DOM element being there at the right time?

I was surprised to see the ember-highcharts addon use scheduleOnce to integrate with Highcharts. I’m curious if there is an alternate way.

I almost always use a modifier for this, rather than a component.

A modifier can only run when the element exists (good for passing that element along to libraries)

An example:

Here, I made a codemirror modifier that loads the codemirror editor on a div element: https://github.com/NullVoxPopuli/limber/blob/main/frontend/app/components/limber/editor/index.gts#L24

<div {{codemirror context.text}}>{{context.text}}</div>

My specific codemirror modifier has some sillyness in it, https://github.com/NullVoxPopuli/limber/blob/main/frontend/app/components/limber/editor/-code-mirror.ts#L21 but the gist is this:

export default class CodeMirror extends Modifier<Signature> {
  @service declare editor: EditorService;
  @service declare router: RouterService;

  modify(element: Element, [value]: PositionalArgs) {
    this.setup(element, [value]);
  }

  isSetup = false;
  setup = async (element: Element, [value]: PositionalArgs) => {
    if (this.isSetup) {
      return;
    }

    // CODEMIRROR here is a function I abstracted for setting up / configuring codemirror.
    // the important part is that the `element` is passed to it, and I can cleanup when the 
    // modifier is destroyed (see registerDestructor below)
    let { view } = CODEMIRROR(element, value);

    registerDestructor(this, () => view.destroy());
  };
}

A code editor doing things with DOM “should” be no different than highcharts operating on DOM, so I hope this technique helps!

1 Like

Brilliant! That exposes another one of my assumptions: I should be using a component :slight_smile:

Agreed. This is quite helpful. Thank you @NullVoxPopuli.

I was surprised to see the ember-highcharts addon use scheduleOnce to integrate with Highcharts. I’m curious if there is an alternate way.

Yeah, this bit me recently. It’s not really correct. I think it works that way because it has evolved over time since before Ember had modifiers, but NullVoxPopuli is correct that in current Ember idioms a modifier is the best way to do this and not need any weird runloop interactions.

The symptom I hit by the way is that the body scroll position was getting messed up when a chart would get rendered. It was happening because the page actually renders “short” with an empty chart briefly due to the scheduleOnce shenanigans.

2 Likes

Ah! That action at a distance gremlin.

This is helpful to store away for when I puzzle through why something might not work in our application’s data visualization sections.

For reference, you can check ember-website, which uses a modifier to create charts with Highcharts.

1 Like