DevExtreme v24.1 is now available.

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

Your search did not match any results.

Angular Toast - Stack

The DevExtreme Toast components can stack multiple notifications. Use the notify(message, stack) or notify(options, stack) method to display stacked messages.

These methods use a stack object that has the following structure: {position, direction}.

Backend API
<div class="options"> <div>Position</div> <dx-radio-group layout="horizontal" [items]="['predefined', 'coordinates']" value="predefined" (onValueChanged)="radioGroupValueChanged($event)" > </dx-radio-group> <dx-select-box [items]="positions" [inputAttr]="{ 'aria-label': 'Position' }" [(value)]="predefinedPosition" [visible]="isPredefined" > </dx-select-box> <div class="section"> <dx-number-box [visible]="!isPredefined" placeholder="top" [(value)]="coordinatePosition.top" width="48%" valueChangeEvent="keyup" [disabled]="!!coordinatePosition.bottom" [inputAttr]="{ 'aria-label': 'Position Top' }" > </dx-number-box> <dx-number-box [visible]="!isPredefined" placeholder="bottom" [(value)]="coordinatePosition.bottom" width="48%" valueChangeEvent="keyup" [disabled]="!!coordinatePosition.top" [inputAttr]="{ 'aria-label': 'Position Bottom' }" > </dx-number-box> </div> <div class="section"> <dx-number-box [visible]="!isPredefined" placeholder="left" [(value)]="coordinatePosition.left" width="48%" valueChangeEvent="keyup" [disabled]="!!coordinatePosition.right" [inputAttr]="{ 'aria-label': 'Position Left' }" > </dx-number-box> <dx-number-box [visible]="!isPredefined" placeholder="right" [(value)]="coordinatePosition.right" width="48%" valueChangeEvent="keyup" [disabled]="!!coordinatePosition.left" [inputAttr]="{ 'aria-label': 'Position Right' }" > </dx-number-box> </div> <div>Direction</div> <dx-select-box [items]="directions" [(value)]="direction" [inputAttr]="{ 'aria-label': 'Direction' }" > </dx-select-box> <div class="section"> <dx-button text="Show" width="48%" (onClick)="show()"></dx-button> <dx-button text="Hide all" width="48%" (onClick)="hideAll()"></dx-button> </div> </div>
import { NgModule, Component, enableProdMode } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { DxButtonModule, DxRadioGroupModule, DxSelectBoxModule, DxNumberBoxModule, } from 'devextreme-angular'; import notify from 'devextreme/ui/notify'; import hideToasts from 'devextreme/ui/toast/hide_toasts'; if (!/localhost/.test(document.location.host)) { enableProdMode(); } type Position = Parameters<typeof notify>[1]['position']; type Direction = Parameters<typeof notify>[1]['direction']; @Component({ selector: 'demo-app', templateUrl: `app/app.component.html`, styleUrls: [`app/app.component.css`], }) export class AppComponent { types: string[] = ['error', 'info', 'success', 'warning']; positions: Position[] = [ 'top left', 'top center', 'top right', 'bottom left', 'bottom center', 'bottom right', 'left center', 'center', 'right center', ]; directions: Direction[] = [ 'down-push', 'down-stack', 'up-push', 'up-stack', 'left-push', 'left-stack', 'right-push', 'right-stack', ]; id = 1; isPredefined = true; predefinedPosition: Position = 'bottom center'; coordinatePosition = { top: undefined, bottom: undefined, left: undefined, right: undefined, }; direction: Direction = 'up-push'; show() { const position = this.isPredefined ? this.predefinedPosition : this.coordinatePosition; const direction = this.direction; notify({ message: `Toast ${this.id}`, height: 45, width: 150, minWidth: 150, type: this.types[Math.floor(Math.random() * 4)], displayTime: 3500, animation: { show: { type: 'fade', duration: 400, from: 0, to: 1, }, hide: { type: 'fade', duration: 40, to: 0 }, }, }, { position, direction }); this.id += 1; } hideAll() { hideToasts(); } radioGroupValueChanged({ value }) { this.isPredefined = value === 'predefined'; } } @NgModule({ imports: [ BrowserModule, DxButtonModule, DxRadioGroupModule, DxSelectBoxModule, DxNumberBoxModule, ], declarations: [AppComponent], bootstrap: [AppComponent], }) export class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule);
.options { padding: 20px; background-color: rgba(191, 191, 191, 0.15); position: absolute; right: 0; top: 0; width: 260px; display: flex; flex-direction: column; gap: 5px; } .section { width: 100%; display: flex; justify-content: space-between; }
// 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', 'chart', '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', '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', 'speed-dial-action', 'splitter', '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/compiler': { 'esModule': true, }, '@angular/animations': { 'esModule': true, }, '@angular/forms': { 'esModule': true, }, }, paths: { 'npm:': 'https://unpkg.com/', 'bundles:': '../../../../bundles/', }, map: { 'ts': 'npm:plugin-typescript@4.2.4/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.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/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@24.1.7/cjs', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.13', 'devextreme/bundles/dx.all': 'npm:devextreme@24.1.7/bundles/dx.all.js', 'devextreme-quill': 'npm:devextreme-quill@1.7.1/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.13', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.56', /* devextreme-angular umd maps */ 'devextreme-angular': 'bundles:devextreme-angular/devextreme-angular.umd.js', 'devextreme-angular/core': 'bundles:devextreme-angular/devextreme-angular-core.umd.js', 'devextreme-angular/http': 'bundles:devextreme-angular/devextreme-angular-http.umd.js', ...componentNames.reduce((acc, name) => { acc[`devextreme-angular/ui/${name}`] = `bundles:devextreme-angular/devextreme-angular-ui-${name}.umd.js`; return acc; }, {}), 'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js', 'tslib': 'npm:tslib@2.6.1/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', '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.8/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.8/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.13/inferno/package.json', 'npm:rxjs@7.5.3/package.json', 'npm:rxjs@7.5.3/operators/package.json', 'npm:devexpress-diagram@2.2.13/package.json', 'npm:devexpress-gantt@4.1.56/package.json', ], }; System.config(window.config); // System.import('@angular/compiler').catch(console.error.bind(console));
<!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/24.1.7/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.13.3/bundles/zone.umd.min.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>

Specify Position

You can set the position field to a string (select 'predefined' in the radio group) or an object (select 'coordinates' in the radio group). Note that if you use coordinates for the position field, you need to specify one vertical and one horizontal coordinate only. For example, if you specify 'top', the demo disables the 'bottom' field, and vice versa.

Specify Direction

The direction field specifies two options: which way the notification stack grows and whether new notifications appear at the end or at the beginning of the line. For this reason, the field's pull-down menu choices show pairs of values such as 'up-push' and 'up-stack'.

  • 'up-push'
    New toasts push the previous toasts upwards.

  • 'up-stack'
    Toasts stack on top of each other.

Hide Toasts

To hide all toast messages, use the hideToasts method.