DevExtreme v23.2 is now available.

Explore our newest features/capabilities and share your thoughts with us.

Your search did not match any results.

Custom Annotations

Annotations are designed to show additional information about visualized data. You can display images, text blocks, and custom content in annotations. To create custom annotations, specify the following Chart properties.

annotations[]

The annotations array specifies a collection of annotations. This demo uses a data source array to create this collection. Each object has the argument property and a data object. The argument property positions an annotation at a specific argument, and the data object stores data from a data source.

You can use the annotations array to configure each annotation individually.

commonAnnotationSettings

Use the commonAnnotationSettings object to specify settings common to all annotations in the chart. In this object, assign custom to the type property. To anchor annotations to a series point, specify the series property. Finally, declare the SVG markup in the annotation template.

Note that individual annotation settings override the common annotation settings.

customizeAnnotation

You can also implement the customizeAnnotation function to customize an individual chart annotation.

en.wikipedia.org
Backend API
<dx-chart id="chart" [dataSource]="populationData"> <dxo-title text="Top 5 Most Populated States in US"> </dxo-title> <dxi-series type="bar" name="Population" argumentField="name" valueField="population" > </dxi-series> <dxo-common-annotation-settings type="custom" series="Population" template="template" [allowDragging]="true" > </dxo-common-annotation-settings> <dxi-annotation *ngFor="let data of populationData" [data]="data" [argument]="data.name" > </dxi-annotation> <dxo-legend [visible]="false"></dxo-legend> <svg *dxTemplate="let annotation of 'template'" class="annotation"> <image attr.href="{{ getImagePath(annotation) }}" width="60" height="40" /> <rect x="0" y="0" class="border" /> <text x="70" y="25" class="state">{{ annotation.argument }}</text> <text x="0" y="60"> <tspan class="caption">Capital:</tspan> <tspan class="capital" dx="5">{{ annotation.data.capital }}</tspan> <tspan dy="14" x="0" class="caption">Population:</tspan> <!-- display: inline --> <tspan class="population" dx="5">{{ annotation.data.population | number : "3.0-0" }}</tspan> <tspan dy="14" x="0" class="caption">Area:</tspan> <!-- display: inline --> <tspan class="area" dx="5">{{ annotation.data.area | number : "3.0-0" }}</tspan> <tspan dx="5">km</tspan> <tspan class="sup" dy="-2">2</tspan> </text> </svg> </dx-chart>
import { NgModule, Component, enableProdMode } from '@angular/core'; import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { DxChartModule } from 'devextreme-angular'; import { Service, State } from './app.service'; if (!/localhost/.test(document.location.host)) { enableProdMode(); } @Component({ selector: 'demo-app', providers: [Service], templateUrl: 'app/app.component.html', styleUrls: ['app/app.component.css'], }) export class AppComponent { populationData: State[]; constructor(service: Service) { this.populationData = service.getPopulationData(); } getImagePath(annotation) { const name = annotation.argument.replace(/\s/, ''); return `../../../../images/flags/${name}.svg`; } } @NgModule({ imports: [ BrowserModule, BrowserTransferStateModule, DxChartModule, ], declarations: [AppComponent], bootstrap: [AppComponent], }) export class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule);
::ng-deep #chart { height: 440px; } ::ng-deep .annotation { font-size: 12px; } ::ng-deep .border { width: 60px; height: 40px; stroke: rgba(191, 191, 191, 0.25); stroke-width: 1px; fill: transparent; } ::ng-deep .state { font-weight: 500; font-size: 14px; } ::ng-deep .caption { font-weight: 500; } ::ng-deep .sup { font-size: 0.8em; }
import { Injectable } from '@angular/core'; export interface State { name: string; population: number; capital: string; area: number; } const populationData: State[] = [{ name: 'California', population: 38802500, capital: 'Sacramento', area: 423967, }, { name: 'Texas', population: 26956958, capital: 'Austin', area: 695662, }, { name: 'Florida', population: 19893297, capital: 'Tallahassee', area: 170312, }, { name: 'New York', population: 19746227, capital: 'Albany', area: 141297, }, { name: 'Illinois', population: 12880580, capital: 'Springfield', area: 149995, }]; @Injectable() export class Service { getPopulationData(): State[] { return populationData; } }
// In real applications, you should not transpile code in the browser. // You can see how to create your own application with Angular and DevExtreme here: // https://js.devexpress.com/Documentation/Guide/Angular_Components/Getting_Started/Create_a_DevExtreme_Application/ window.exports = window.exports || {}; window.config = { transpiler: 'ts', typescriptOptions: { module: 'system', emitDecoratorMetadata: true, experimentalDecorators: true, }, meta: { 'typescript': { 'exports': 'ts', }, 'devextreme/time_zone_utils.js': { 'esModule': true, }, 'devextreme/localization.js': { 'esModule': true, }, 'devextreme/viz/palette.js': { 'esModule': true, }, }, paths: { 'npm:': 'https://unpkg.com/', }, map: { 'ts': 'npm:plugin-typescript@4.2.4/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.js', '@angular/core': 'npm:@angular/core@12.2.17', '@angular/platform-browser': 'npm:@angular/platform-browser@12.2.17', '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic@12.2.17', '@angular/forms': 'npm:@angular/forms@12.2.17', '@angular/common': 'npm:@angular/common@12.2.17', '@angular/compiler': 'npm:@angular/compiler@12.2.17', 'tslib': 'npm:tslib@2.6.2/tslib.js', 'rxjs': 'npm:rxjs@7.5.3/dist/bundles/rxjs.umd.js', 'rxjs/operators': 'npm:rxjs@7.5.3/dist/cjs/operators/index.js', 'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js', 'luxon': 'npm:luxon@1.28.1/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign@1.1.0', 'devextreme': 'npm:devextreme@23.2.5/cjs', 'devextreme/bundles/dx.all': 'npm:devextreme@23.2.5/bundles/dx.all.js', 'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js', 'devextreme-quill': 'npm:devextreme-quill@1.6.4/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.5', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.51', 'devextreme-angular': 'npm:devextreme-angular@23.2.5', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.12', 'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js', 'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js', 'inferno-create-element': 'npm:inferno-create-element@7.4.11/dist/inferno-create-element.min.js', 'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js', 'inferno-hydrate': 'npm:inferno-hydrate@7.4.11/dist/inferno-hydrate.min.js', 'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js', 'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js', 'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js', // Prettier 'prettier/standalone': 'npm:prettier@2.8.4/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.4/parser-html.js', }, packages: { 'app': { main: './app.component.ts', defaultExtension: 'ts', }, 'devextreme': { defaultExtension: 'js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, 'rxjs': { defaultExtension: 'js', }, 'rxjs/operators': { defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.12/inferno/package.json', 'npm:@angular/*/package.json', 'npm:@angular/common@12.2.17/*/package.json', 'npm:rxjs@7.5.3/package.json', 'npm:rxjs@7.5.3/operators/package.json', 'npm:devextreme-angular@23.2.5/*/package.json', 'npm:devextreme-angular@23.2.5/ui/*/package.json', 'npm:devextreme-angular@23.2.5/package.json', 'npm:devexpress-diagram@2.2.5/package.json', 'npm:devexpress-gantt@4.1.51/package.json', ], }; System.config(window.config);
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/23.2.5/css/dx.light.css" /> <script src="https://unpkg.com/core-js@2.6.12/client/shim.min.js"></script> <script src="https://unpkg.com/zone.js@0.12.0/dist/zone.js"></script> <script src="https://unpkg.com/reflect-metadata@0.1.13/Reflect.js"></script> <script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script> <script src="config.js"></script> <script> System.import("app").catch(console.error.bind(console)); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <demo-app>Loading...</demo-app> </div> </body> </html>