Skip to content

Angular

Angular uses the <chartbuddy-insight> custom element rather than a compiled Angular library. Angular binds properties and listens to events on custom elements natively, so you get the same lifecycle guarantees as the React and Vue bindings.

bash
npm install @chartbuddy.io/embed

No extra peer dependency — the element is plain DOM.

Why an element instead of an Angular package

A native Angular library has to be compiled with Angular's own toolchain and published against a specific Angular major, which would tie your ChartBuddy upgrades to your Angular upgrades. The element has no such coupling and works unchanged across Angular versions. If you would rather have typed Angular inputs, the thin wrapper below is about twenty lines and lives in your codebase, where it can follow your Angular version.

Setup

Import once — importing registers the element — and allow custom element tags in the components that use it.

ts
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import '@chartbuddy.io/embed/element';

@Component({
  selector: 'app-revenue',
  standalone: true,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `
    <chartbuddy-insight
      [chartData]="chartData"
      instance-id="revenue"
      style="display:block; height:400px"
      (change)="onChange($event)"
    ></chartbuddy-insight>
  `,
})
export class RevenueComponent {
  chartData = {
    chartType: 'clusteredBar',
    isDataTransposed: true,
    seriesData: [['', 'Q1', 'Q2'], ['Revenue', 100, 112]],
  };

  onChange(event: Event) {
    const cd = (event as CustomEvent).detail;
    // …
  }
}

For an NgModule app, put CUSTOM_ELEMENTS_SCHEMA in the module's schemas instead.

The chart fills its container, so give it a height.

Properties vs attributes

chartData is an object, so it must be set as a property[chartData]="…" with square brackets. Angular sets DOM properties for bracket bindings on custom elements, so this works as written.

OptionBinding
chartData[chartData]="cd" — property only
instanceIdinstance-id="revenue" or [instanceId]="id"
editableeditable / [editable]="true"
toolbartoolbar / [toolbar]="flag"
persist[persist]="false"
assetBaseasset-base="https://…/"

Assigning a new object to chartData patches the live chart via update(). Changing any of the others remounts it. A burst of attribute changes in the same tick is coalesced into a single remount.

Mutation is not detected

chartData is compared by identity. With Angular's default change detection you must assign a new object:

ts
// Not picked up
this.chartData.title.text = 'FY26';

// Picked up
this.chartData = { ...this.chartData, title: { text: 'FY26' } };

This also keeps you compatible with OnPush.

Events

All four are CustomEvents; read event.detail.

Eventdetail
readyThe Insight instance
changeChartData | null
mode'view' | 'edit'
errorError — a ChartDataValidationError for invalid config

Note that error is a CustomEvent named error, not an ErrorEvent, and it does not bubble.

Imperative access

Grab the element with a template ref and read .insight:

ts
@ViewChild('chart') chartRef!: ElementRef<HTMLElement & { insight: any; ready: boolean }>;

async downloadPng() {
  const { insight } = this.chartRef.nativeElement;
  if (insight) await insight.downloadPng();
}
html
<chartbuddy-insight #chart [chartData]="chartData" style="display:block; height:400px" />

insight is null until the mount resolves; ready tells you when it has.

Optional: typed wrapper component

If you want Angular-native inputs and outputs, wrap the element once:

ts
import {
  Component,
  CUSTOM_ELEMENTS_SCHEMA,
  EventEmitter,
  Input,
  Output,
} from '@angular/core';
import '@chartbuddy.io/embed/element';
import type { ChartData, ChartDataInput } from '@chartbuddy.io/embed';

@Component({
  selector: 'cb-insight',
  standalone: true,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `
    <chartbuddy-insight
      [chartData]="chartData"
      [instanceId]="instanceId"
      [editable]="editable"
      style="display:block; height:100%"
      (change)="change.emit($any($event).detail)"
      (error)="error.emit($any($event).detail)"
    ></chartbuddy-insight>
  `,
})
export class CbInsightComponent {
  @Input() chartData?: ChartDataInput;
  @Input() instanceId?: string;
  @Input() editable = false;
  @Output() change = new EventEmitter<ChartData | null>();
  @Output() error = new EventEmitter<Error>();
}

Then <cb-insight [chartData]="cd" (change)="save($event)" /> with no schema boilerplate at each call site.

Handling invalid data

Chart data is validated at the API boundary. Invalid config fires the error event instead of rendering a blank chart:

ts
import { ChartDataValidationError, validateChartData } from '@chartbuddy.io/embed';

onError(event: Event) {
  const err = (event as CustomEvent).detail;
  if (err instanceof ChartDataValidationError) {
    this.issues = err.errors; // [{ path, code, expected, suggestion }, …]
  }
}

You can also check before binding, with no chart involved:

ts
const { valid, errors } = validateChartData(candidate);

See Validation.

Dashboards

One element per chart, each with a stable instance-id. Duplicate ids on a page throw, so derive them from your data rather than the loop index.

html
@for (panel of panels; track panel.id) {
  <chartbuddy-insight
    [chartData]="panel.chartData"
    [instanceId]="panel.id"
    [persist]="false"
    style="display:block; height:300px"
  ></chartbuddy-insight>
}

The engine loads once per page, not once per chart — but read Installation before putting a lot of charts on one screen.

SSR

The element needs a real DOM, so don't import it on the server. Under Angular Universal, import it inside a browser-only guard:

ts
if (isPlatformBrowser(this.platformId)) {
  await import('@chartbuddy.io/embed/element');
}

There is no headless/Node render path today, so charts cannot be pre-rendered to PNG on a server.

Other frameworks

The same element works in Svelte, Solid, Lit, Astro, and plain HTML:

html
<script type="module">
  import 'https://unpkg.com/@chartbuddy.io/embed/element.mjs';
  const el = document.querySelector('chartbuddy-insight');
  el.chartData = { chartType: 'pie', seriesData: [['Category', 'Value'], ['A', 3]] };
</script>

<chartbuddy-insight style="display:block; height:400px"></chartbuddy-insight>

Developer & LLM documentation · Not the end-user Help Center · Help Center