Backend API
<dx-pie-chart id="pie" palette="Vintage" [dataSource]="champions">
<dxo-pie-chart-common-annotation-settings
type="image"
color="transparent"
[paddingLeftRight]="0"
[paddingTopBottom]="0"
tooltipTemplate="tooltipTemplate"
>
<dxo-pie-chart-image [height]="60" [width]="90"></dxo-pie-chart-image>
<dxo-pie-chart-border color="transparent"></dxo-pie-chart-border>
</dxo-pie-chart-common-annotation-settings>
@for(item of annotations; track item.country) {
<dxi-pie-chart-annotation
[argument]="item.country"
[data]="item.data"
[location]="item.location"
[offsetX]="item.offsetX"
[offsetY]="item.offsetY"
[color]="item.color"
>
<dxo-pie-chart-image [url]="item.image"></dxo-pie-chart-image>
<dxo-pie-chart-border [color]="item.borderColor"></dxo-pie-chart-border>
<dxo-pie-chart-shadow [opacity]="item.shadowOpacity"></dxo-pie-chart-shadow>
</dxi-pie-chart-annotation>
}
<dxi-pie-chart-series argumentField="country" valueField="gold">
<dxo-pie-chart-label
[visible]="true"
position="inside"
[radialOffset]="83"
backgroundColor="transparent"
>
<dxo-pie-chart-font [size]="16" [weight]="600"></dxo-pie-chart-font>
</dxo-pie-chart-label>
</dxi-pie-chart-series>
<dxo-pie-chart-tooltip
[paddingLeftRight]="12"
[paddingTopBottom]="10"
></dxo-pie-chart-tooltip>
<dxo-pie-chart-legend [visible]="false"></dxo-pie-chart-legend>
<dxo-pie-chart-title
text="Ice Hockey World Championship Gold Medal Winners"
></dxo-pie-chart-title>
<div *dxTemplate="let annotation of 'tooltipTemplate'" class="medal-tooltip">
<div class="country-name"
>{{ annotation.data.country }}
@if(annotation.data.oldCountryName) {
<br />{{ annotation.data.oldCountryName }}
}
</div>
<div class="gold">
<span class="caption">Gold</span>: {{ annotation.data.gold }}
</div>
<div class="silver">
<span class="caption">Silver</span>: {{ annotation.data.silver }}
</div>
<div class="bronze">
<span class="caption">Bronze</span>: {{ annotation.data.bronze }}
</div>
</div>
</dx-pie-chart>
import { bootstrapApplication } from '@angular/platform-browser';
import { Component, enableProdMode, provideZoneChangeDetection } from '@angular/core';
import { DxPieChartModule } from 'devextreme-angular';
import { MedalistData, AnnotationInfo, Service } from './app.service';
if (!/localhost/.test(document.location.host)) {
enableProdMode();
}
let modulePrefix = '';
// @ts-ignore
if (window && window.config?.packageConfigPaths) {
modulePrefix = '/app';
}
@Component({
selector: 'demo-app',
templateUrl: `app/app.component.html`,
styleUrls: [`app/app.component.css`],
providers: [Service],
imports: [
DxPieChartModule,
],
})
export class AppComponent {
champions: MedalistData[];
annotations: AnnotationInfo[];
constructor(service: Service) {
this.champions = service.getChampionsData();
this.annotations = service.getAnnotationSources();
}
}
bootstrapApplication(AppComponent, {
providers: [
provideZoneChangeDetection({ eventCoalescing: true, runCoalescing: true }),
],
});
::ng-deep #pie {
height: 440px;
}
::ng-deep .medal-tooltip {
font-size: 12px;
}
::ng-deep .medal-tooltip .country-name {
font-size: 14px;
font-weight: 700;
margin: 5px 0 10px;
}
::ng-deep .medal-tooltip .caption {
font-weight: 600;
}
import {
Injectable,
} from '@angular/core';
export class MedalistData {
country?: string;
oldCountryName?: string;
gold?: number;
silver?: number;
bronze?: number;
}
export class AnnotationInfo {
country: string;
data?: MedalistData;
image?: string;
location?: string;
offsetX?: number;
offsetY?: number;
color?: string;
borderColor?: string;
shadowOpacity?: number;
}
const champions: MedalistData[] = [{
country: 'Russia',
oldCountryName: 'Soviet Union',
gold: 27,
silver: 10,
bronze: 10,
}, {
country: 'Canada',
gold: 26,
silver: 15,
bronze: 9,
}, {
country: 'Czech Republic',
oldCountryName: 'Czechoslovakia',
gold: 12,
silver: 13,
bronze: 21,
}, {
country: 'Sweden',
gold: 11,
silver: 19,
bronze: 17,
}, {
country: 'Finland',
gold: 3,
silver: 8,
bronze: 3,
}, {
country: 'United States',
gold: 2,
silver: 9,
bronze: 8,
}, {
country: 'Great Britain',
gold: 1,
silver: 2,
bronze: 2,
}, {
country: 'Slovakia',
gold: 1,
silver: 2,
bronze: 1,
}];
const annotationSources: AnnotationInfo[] = [{
country: 'Russia',
offsetX: 15,
offsetY: 5,
}, {
country: 'Canada',
offsetY: 10,
}, {
country: 'Czech Republic',
offsetX: -5,
offsetY: -35,
}, {
country: 'Sweden',
offsetX: 20,
offsetY: -25,
}, {
country: 'Finland',
location: 'edge',
offsetX: 45,
offsetY: -85,
}, {
country: 'United States',
location: 'edge',
offsetX: 85,
offsetY: -45,
}, {
country: 'Great Britain',
location: 'edge',
offsetX: 81,
offsetY: 15,
}, {
country: 'Slovakia',
location: 'edge',
offsetX: 45,
offsetY: 80,
}];
const edgeAnnotationSettings = {
color: '#aaaaaa',
borderColor: '#aaaaaa',
shadowOpacity: 0.3,
};
@Injectable()
export class Service {
getChampionsData(): MedalistData[] {
return champions;
}
getAnnotationSources(): AnnotationInfo[] {
const annotations: AnnotationInfo[] = [];
for (let i = 0; i < annotationSources.length; i += 1) {
const annotation = annotationSources[i];
const country = annotation.country;
const image = `../../../../images/flags/3x2/${country.replace(/\s/, '')}.svg`;
const data = { ...champions.filter((d) => (d.country === country))[0] };
annotations.push({
...annotation,
image,
data,
...(annotation.location === 'edge' ? edgeAnnotationSettings : {}),
});
}
return annotations;
}
}
// 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/
const componentNames = [
'accordion',
'action-sheet',
'autocomplete',
'bar-gauge',
'box',
'bullet',
'button-group',
'button',
'calendar',
'card-view',
'chart',
'chat',
'check-box',
'circular-gauge',
'color-box',
'context-menu',
'data-grid',
'date-box',
'date-range-box',
'defer-rendering',
'diagram',
'draggable',
'drawer',
'drop-down-box',
'drop-down-button',
'file-manager',
'file-uploader',
'filter-builder',
'form',
'funnel',
'gallery',
'gantt',
'html-editor',
'linear-gauge',
'list',
'load-indicator',
'load-panel',
'lookup',
'map',
'menu',
'multi-view',
'nested',
'number-box',
'pagination',
'pie-chart',
'pivot-grid-field-chooser',
'pivot-grid',
'polar-chart',
'popover',
'popup',
'progress-bar',
'radio-group',
'range-selector',
'range-slider',
'recurrence-editor',
'resizable',
'responsive-box',
'sankey',
'scheduler',
'scroll-view',
'select-box',
'slider',
'sortable',
'sparkline',
'speech-to-text',
'speed-dial-action',
'splitter',
'stepper',
'switch',
'tab-panel',
'tabs',
'tag-box',
'text-area',
'text-box',
'tile-view',
'toast',
'toolbar',
'tooltip',
'tree-list',
'tree-map',
'tree-view',
'validation-group',
'validation-summary',
'validator',
'vector-map',
];
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,
},
'@angular/platform-browser-dynamic': {
'esModule': true,
},
'@angular/platform-browser': {
'esModule': true,
},
'@angular/core': {
'esModule': true,
},
'@angular/common': {
'esModule': true,
},
'@angular/common/http': {
'esModule': true,
},
'@angular/animations': {
'esModule': true,
},
'@angular/forms': {
'esModule': true,
},
'openai': {
'esModule': true,
},
},
paths: {
'npm:': 'https://cdn.jsdelivr.net/npm/',
'bundles:': '../../../../bundles/',
'externals:': '../../../../bundles/externals/',
'anti-forgery:': '../../../../shared/anti-forgery/',
},
map: {
'anti-forgery': 'anti-forgery:fetch-override.js',
'ts': 'npm:plugin-typescript@8.0.0/lib/plugin.js',
'typescript': 'npm:typescript@4.2.4/lib/typescript.js',
'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js',
/* @angular */
'@angular/compiler': 'bundles:@angular/compiler.umd.js',
'@angular/platform-browser-dynamic': 'bundles:@angular/platform-browser-dynamic.umd.js',
'@angular/core': 'bundles:@angular/core.umd.js',
'@angular/core/primitives/signals': 'bundles:@angular/core.primitives.signals.umd.js',
'@angular/core/primitives/di': 'bundles:@angular/core.primitives.di.umd.js',
'@angular/common': 'bundles:@angular/common.umd.js',
'@angular/common/http': 'bundles:@angular/common-http.umd.js',
'@angular/platform-browser': 'bundles:@angular/platform-browser.umd.js',
'@angular/platform-browser/animations': 'bundles:@angular/platform-browser.umd.js',
'@angular/forms': 'bundles:@angular/forms.umd.js',
/* devextreme */
'devextreme': 'npm:devextreme@link:../../packages/devextreme/artifacts/npm/devextreme/cjs',
'devextreme-quill': 'npm:devextreme-quill@1.7.8/dist/dx-quill.min.js',
'devexpress-diagram': 'npm:devexpress-diagram@2.2.25',
'devexpress-gantt': 'npm:devexpress-gantt@4.1.66',
/* devextreme-angular umd maps */
'devextreme-angular': 'bundles:devextreme-angular/devextreme-angular.umd.js',
'devextreme-angular/common/ai-integration': 'bundles:devextreme-angular/devextreme-angular-common-ai-integration.umd.js',
'devextreme-angular/core': 'bundles:devextreme-angular/devextreme-angular-core.umd.js',
'devextreme-angular/common/charts': 'bundles:devextreme-angular/devextreme-angular-common-charts.umd.js',
'devextreme-angular/common/core/animation': 'bundles:devextreme-angular/devextreme-angular-common-core-animation.umd.js',
'devextreme-angular/common/core/environment': 'bundles:devextreme-angular/devextreme-angular-common-core-environment.umd.js',
'devextreme-angular/common/core/events': 'bundles:devextreme-angular/devextreme-angular-common-core-events.umd.js',
'devextreme-angular/common/core/localization': 'bundles:devextreme-angular/devextreme-angular-common-core-localization.umd.js',
'devextreme-angular/common/core': 'bundles:devextreme-angular/devextreme-angular-common-core.umd.js',
'devextreme-angular/common/data/custom-store': 'bundles:devextreme-angular/devextreme-angular-common-data-custom-store.umd.js',
'devextreme-angular/common/data': 'bundles:devextreme-angular/devextreme-angular-common-data.umd.js',
'devextreme-angular/common/export/excel': 'bundles:devextreme-angular/devextreme-angular-common-export-excel.umd.js',
'devextreme-angular/common/export/pdf': 'bundles:devextreme-angular/devextreme-angular-common-export-pdf.umd.js',
'devextreme-angular/common/export': 'bundles:devextreme-angular/devextreme-angular-common-export.umd.js',
'devextreme-angular/common/grids': 'bundles:devextreme-angular/devextreme-angular-common-grids.umd.js',
'devextreme-angular/common': 'bundles:devextreme-angular/devextreme-angular-common.umd.js',
'devextreme-angular/http': 'bundles:devextreme-angular/devextreme-angular-http.umd.js',
'devextreme-angular/core/tokens': 'bundles:devextreme-angular/devextreme-angular-core-tokens.umd.js',
...componentNames.reduce((acc, name) => {
acc[`devextreme-angular/ui/${name}`] = `bundles:devextreme-angular/devextreme-angular-ui-${name}.umd.js`;
acc[`devextreme-angular/ui/${name}/nested`] = `bundles:devextreme-angular/devextreme-angular-ui-${name}-nested.umd.js`;
return acc;
}, {}),
'tslib': 'npm:tslib/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@3.4.4/build/global/luxon.min.js',
'es6-object-assign': 'npm:es6-object-assign',
'inferno': 'npm:inferno@8.2.3/dist/inferno.min.js',
'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js',
'inferno-create-element': 'npm:inferno-create-element@8.2.3/dist/inferno-create-element.min.js',
'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js',
'inferno-hydrate': 'npm:inferno-hydrate/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',
'@preact/signals-core': 'npm:@preact/signals-core@1.8.0/dist/signals-core.min.js',
// Prettier
'prettier/standalone': 'npm:prettier@2.8.8/standalone.js',
'prettier/parser-html': 'npm:prettier@2.8.8/parser-html.js',
'zone.js': 'npm:zone.js@0.15.1/bundles/zone.umd.js',
},
packages: {
'app': {
main: './app.component.ts',
defaultExtension: 'ts',
},
'devextreme': {
defaultExtension: 'js',
},
'devextreme/events/utils': {
main: 'index',
},
'devextreme/common/core/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:rxjs@7.5.3/package.json',
'npm:rxjs@7.5.3/operators/package.json',
'npm:devexpress-diagram@2.2.25/package.json',
'npm:devexpress-gantt@4.1.66/package.json',
],
};
window.process = {
env: {
NODE_ENV: 'production',
},
};
System.config(window.config);
// eslint-disable-next-line no-console
// System.import('@angular/compiler').catch(console.error.bind(console));
// eslint-disable-next-line
const useTgzInCSB = ['openai'];
let packagesInfo = {
"@angular/core": {
"version": "21.0.7"
},
"core-js": {
"version": "2.6.12"
},
"typescript": {
"version": "5.9.3"
},
"zone.js": {
"version": "0.15.1"
}
};
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<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=5.0" />
<link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/25.2.5/css/dx.light.css" />
<script src="https://cdn.jsdelivr.net/npm/core-js@2.6.12/client/shim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/zone.js@0.15.1/bundles/zone.umd.js"></script>
<script src="https://cdn.jsdelivr.net/npm/reflect-metadata@0.1.13/Reflect.js"></script>
<script src="https://cdn.jsdelivr.net/npm/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>
You can set each annotation type property to "text", "image", or "custom". In this demo, the type used for all annotations is "image".
Annotations can deliver more information if you add tooltips. A tooltip appears when users hover the mouse pointer over an annotation. This demo illustrates how you can implement a tooltip with custom content via the tooltipTemplate property.
For more information on annotation settings, refer to the annotations[] help topic.