DevExtreme v24.1 is now available.

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

Your search did not match any results.

Mentions

Our HtmlEditor allows end-users to insert "mentions" and reference others within text or conversation threads as demonstrated in this demo. With this feature, you can introduce functionality that emulates capabilities found in collaboration tools like Microsoft Teams.

Backend API
<div id="chat-window"> <div class="message" tabindex="0"> <div class="photo"> <img alt="Kevin Carter" src="../../../../images/mentions/Kevin-Carter.png" /> </div> <div class="name"> Kevin Carter </div> <div class="date"> 07/03/19 - 12:22AM </div> <div class="text"> <span class="dx-mention" spellcheck="false" ><span><span>&#64;</span>John Heart</span></span > What experience do you have in this field? </div> </div> <br /> <div class="message" tabindex="0"> <div class="photo"> <img alt="John Heart" src="../../../../images/mentions/John-Heart.png" /> </div> <div class="name"> John Heart </div> <div class="date"> 07/03/19 - 12:25AM </div> <div class="text"> I've been in the audio/video industry since 2001, and I've been the CEO of DevAv since 2009. </div> </div> <br /> <div class="message" tabindex="0"> <div class="photo"> <img alt="Kevin Carter" src="../../../../images/mentions/Kevin-Carter.png" /> </div> <div class="name"> Kevin Carter </div> <div class="date"> 07/03/19 - 12:26AM </div> <div class="text"> That's very interesting. <span class="dx-mention" spellcheck="false" ><span><span>&#64;</span>Olivia Peyton</span></span >, what do you think? </div> </div> </div> <dx-html-editor> <dxi-mention valueExpr="text" displayExpr="text" searchExpr="text" [dataSource]="employees" ></dxi-mention> <p> <span class="dx-mention" spellcheck="false" data-marker="&#64;" data-mention-value="Kevin Carter" ><span><span>&#64;</span>Kevin Carter</span></span > I think John's expertise can be very valuable in our startup. </p> </dx-html-editor>
import { NgModule, Component, enableProdMode } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { DxHtmlEditorModule } from 'devextreme-angular'; import { Employee, Service } from './app.service'; if (!/localhost/.test(document.location.host)) { enableProdMode(); } @Component({ selector: 'demo-app', templateUrl: 'app/app.component.html', styleUrls: ['app/app.component.css'], providers: [Service], }) export class AppComponent { employees: Employee[]; constructor(service: Service) { this.employees = service.getEmployees(); } } @NgModule({ imports: [ BrowserModule, DxHtmlEditorModule, ], declarations: [AppComponent], bootstrap: [AppComponent], }) export class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule);
::ng-deep #chat-window { background-color: rgba(191, 191, 191, 0.15); height: 350px; padding: 10px; } ::ng-deep .dx-htmleditor { height: 100px; } ::ng-deep #send { float: right; margin: 10px 0; } ::ng-deep .message { height: 85px; background-color: var(--dx-component-color-bg); display: inline-block; max-width: 60%; border-radius: 5px; margin: 5px; padding: 10px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); } ::ng-deep .name { font-weight: bold; } ::ng-deep .date { opacity: 0.5; } ::ng-deep .photo { float: left; height: 100%; } ::ng-deep .photo > img { width: 40px; height: 40px; display: block; border-radius: 40px; margin-right: 10px; }
import { Injectable } from '@angular/core'; export class Employee { text: string; team: string; icon: string; } const employees: Employee[] = [{ text: 'John Heart', team: 'Engineering', icon: '../../../../images/mentions/John-Heart.png', }, { text: 'Kevin Carter', team: 'Engineering', icon: '../../../../images/mentions/Kevin-Carter.png', }, { text: 'Olivia Peyton', team: 'Management', icon: '../../../../images/mentions/Olivia-Peyton.png', }, { text: 'Robert Reagan', team: 'Management', icon: '../../../../images/mentions/Robert-Reagan.png', }, { text: 'Cynthia Stanwick', team: 'Engineering', icon: '../../../../images/mentions/Cynthia-Stanwick.png', }, { text: 'Brett Wade ', team: 'Analysis', icon: '../../../../images/mentions/Brett-Wade.png', }, { text: 'Greta Sims', team: 'QA', icon: '../../../../images/mentions/Greta-Sims.png', }, ]; @Injectable() export class Service { getEmployees() : Employee[] { return employees; } }
// 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/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.3/cjs', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.13', 'devextreme/bundles/dx.all': 'npm:devextreme@24.1.3/bundles/dx.all.js', 'devextreme-quill': 'npm:devextreme-quill@1.7.1/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.8', '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', ...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.8/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.3/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.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>

A "mention" is triggered by a marker. You are free to choose a "marker" of your choice (this demo uses the @ symbol to trigger a "mention"). User names are stored as items in a dataSource. Data can be retrieved from multiple data sources. When using multiple data sources, you must assign an unique marker to each.

To "mention" a user, simply enter the @ symbol and select the desired record from the "Mentions" drop-down user list. Like today's most popular collaboration tools, you can enter additional characters to filter the records displayed within the "Mentions" drop-down user list.