DevExtreme v24.2 is now available.

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

Your search did not match any results.

Angular Html Editor - Tables

DevExtreme HTML Editor allows users to create and manage tables. This demo illustrates table resize support and table management with the toolbar, context menu, and in code.

Backend API
<dx-html-editor height="750px" [value]="valueContent"> <dxo-table-context-menu [enabled]="contextMenuEnabled" ></dxo-table-context-menu> <dxo-table-resizing [enabled]="allowResizing"></dxo-table-resizing> <dxo-toolbar> <dxi-item name="bold"></dxi-item> <dxi-item name="color"></dxi-item> <dxi-item name="separator"></dxi-item> <dxi-item name="alignLeft"></dxi-item> <dxi-item name="alignCenter"></dxi-item> <dxi-item name="alignRight"></dxi-item> <dxi-item name="separator"></dxi-item> <dxi-item name="insertTable"></dxi-item> <dxi-item name="insertHeaderRow"></dxi-item> <dxi-item name="insertRowAbove"></dxi-item> <dxi-item name="insertRowBelow"></dxi-item> <dxi-item name="separator"></dxi-item> <dxi-item name="insertColumnLeft"></dxi-item> <dxi-item name="insertColumnRight"></dxi-item> <dxi-item name="separator"></dxi-item> <dxi-item name="deleteColumn"></dxi-item> <dxi-item name="deleteRow"></dxi-item> <dxi-item name="deleteTable"></dxi-item> <dxi-item name="separator"></dxi-item> <dxi-item name="cellProperties"></dxi-item> <dxi-item name="tableProperties"></dxi-item> </dxo-toolbar> </dx-html-editor> <div class="options"> <div class="caption">Options</div> <div class="option"> <dx-check-box text="Allow Table Resizing" [(value)]="allowResizing" ></dx-check-box> </div> <div class="option"> <dx-check-box text="Enable Table Context Menu" [(value)]="contextMenuEnabled" ></dx-check-box> </div> </div>
import { NgModule, Component, enableProdMode } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { DxHtmlEditorModule, DxCheckBoxModule } from 'devextreme-angular'; import { 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 { valueContent: string; allowResizing = true; contextMenuEnabled = true; constructor(service: Service) { this.valueContent = service.getMarkup(); } } @NgModule({ imports: [ BrowserModule, DxHtmlEditorModule, DxCheckBoxModule, ], declarations: [AppComponent], bootstrap: [AppComponent], }) export class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule);
::ng-deep .demo-container { height: 950px; } .options { padding: 20px; background-color: rgba(191, 191, 191, 0.15); margin-top: 20px; } .caption { font-size: 18px; font-weight: 500; } .option { margin-top: 10px; }
import { Injectable } from '@angular/core'; const markup = ` <h2> Hardware Upgrade </h2> <br> <table> <thead> <tr> <th style="text-align: left; width: 320px;">Task Subject</th> <th style="text-align: center;">Status</th> <th style="text-align: right;">Progress</th> </tr> </thead> <tbody> <tr style="text-align: center;"> <td style="text-align: left; width: 320px;">Approve Personal Computer Upgrade Plan</td> <td style="text-align: center; color: #5cb85c;"> <p style="font-size: 1.15em;">✓</p> <p>Completed</p> </td> <td style="text-align: right;">100%</td> </tr> <tr style="text-align: center;"> <td style="text-align: left; width: 320px;">Prepare workspaces</td> <td style="text-align: center; color: #5cb85c;"> <p style="font-size: 1.15em;">✓</p> <p>Completed</p> </td> <td style="text-align: right;">100%</td> </tr> <tr style="text-align: center;"> <td style="text-align: left; width: 320px;">Upgrade Server Hardware</td> <td style="text-align: center;"> <p style="font-size: 1.15em;">⟳</p> <p>In Progress</p> </td> <td style="text-align: right;">45%</td> </tr> <tr style="text-align: center;"> <td style="text-align: left; width: 320px;">Upgrade Personal Computers</td> <td style="text-align: center; color: #f0ad4e;"> <p style="font-size: 1.15em;">‖</p> <p>Need Assistance</p> </td> <td style="text-align: right;">80%</td> </tr> <tr style="text-align: center;"> <td style="text-align: left; width: 320px;">Replace HDD with SSD</td> <td style="text-align: center;"> <p style="font-size: 1.15em;">⟳</p> <p>In Progress</p> </td> <td style="text-align: right;">80%</td> </tr> <tr style="text-align: center;"> <td style="text-align: left; width: 320px;">Purchase a New Server</td> <td style="text-align: center; color: #d9534f;"> <p style="font-size: 1.15em;">✖</p> <p>Canceled</p> </td> <td style="text-align: right;">15%</td> </tr> <tr style="text-align: center;"> <td style="text-align: left; width: 320px;">Purchase Laptops</td> <td style="text-align: center; color: #5cb85c;"> <p style="font-size: 1.15em;">✓</p> <p>Completed</p> </td> <td style="text-align: right;">100%</td> </tr> <tr style="text-align: center;"> <td style="text-align: left; width: 320px;">Prepare a list of necessary devices for testing</td> <td style="text-align: center; color: #5cb85c;"> <p style="font-size: 1.15em;">✓</p> <p>Completed</p> </td> <td style="text-align: right;">100%</td> </tr> <tr style="text-align: center;"> <td style="text-align: left; width: 320px;">Purchase devices for testing</td> <td style="text-align: center; color: #f0ad4e;"> <p style="font-size: 1.15em;">‖</p> <p>Need Assistance</p> </td> <td style="text-align: right;">25%</td> </tr> <tr style="text-align: center;"> <td style="text-align: left; width: 320px;">Recycle Broken Hardware</td> <td style="text-align: center; color: #5cb85c;"> <p style="font-size: 1.15em;">✓</p> <p>Completed</p> </td> <td style="text-align: right;">100%</td> </tr> </tbody> </table> <br> `; @Injectable() export class Service { getMarkup(): string { return markup; } }
// 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', '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', '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, }, 'openai': { 'esModule': true, }, }, paths: { 'npm:': 'https://unpkg.com/', 'bundles:': '../../../../bundles/', 'externals:': '../../../../bundles/externals/', }, map: { '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/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/runtime': 'npm:@devextreme/runtime@3.0.12', 'devextreme/bundles/dx.all': 'npm:devextreme@link:../../packages/devextreme/artifacts/npm/devextreme/bundles/dx.all.js', 'devextreme-quill': 'npm:devextreme-quill@1.7.1/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.5', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.54', /* 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`; 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@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/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.12/inferno/package.json', 'npm:rxjs@7.5.3/package.json', 'npm:rxjs@7.5.3/operators/package.json', 'npm:devexpress-diagram@2.2.5/package.json', 'npm:devexpress-gantt@4.1.54/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.2.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.14.10/bundles/zone.umd.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>

Manage Tables with the Built-in Toolbar

To manage tables via a built-in toolbar, add the following items to it:

  • "insertTable" / "deleteTable"
    Manage a table.

  • "insertRowAbove" / "insertRowBelow" / "deleteRow"
    Manage rows.

  • "insertColumnLeft" / "insertColumnRight" / "deleteColumn"
    Manage columns.

  • "tableProperties" / "cellProperties"
    Open a pop-up window with table or cell customization options.

Manage Tables with the Context Menu

To manage table layouts with the context menu, set the tableContextMenu.enabled property to true. In this demo, you can click the Enable Table Context Menu check box under the HtmlEditor to enable or disable this property. When it is enabled, right-click a table cell to open the context menu.

NOTE

The context menu cannot be used to create new tables. The menu is only available within table boundaries. If you want users to create tables, add an "insertTable" item to the toolbar.

If you need to customize menu commands, override the tableContextMenu.items array. Refer to its description for a code example.

Manage Tables Programmatically

You can perform table-related actions in code. Use the getModule method to access the Table module. This module contains methods with names that match toolbar item names listed above. Refer to the following topic for the complete method list and code examples: DevExtreme Quill modules: Table.

Resize Tables

If you want to resize table rows and columns, set the tableResizing.enabled property to true. In this demo, you can use the Allow Table Resizing check box to change the property value.

You can also set the tableResizing.minColumnWidth and tableResizing.minRowHeight properties to specify minimum column width and row height. If you do not set these properties, width and height are determined by cell content. This demo illustrates the latter behavior (when you reduce column width or row height to small values).