Skip to main content

Data Grid

In this final step we will build a viewer extension that will provide a datagrid view of our design element properties using the open source Tabulator library.

Extension skeleton

As usual, let's create a new file under the extensions subfolder, call it DataGridExtension.js, and populate it with the following code:

wwwroot/extensions/DataGridExtension.js
import { BaseExtension } from './BaseExtension.js';

class DataGridExtension extends BaseExtension {
constructor(viewer, options) {
super(viewer, options);
}

async load() {
super.load();
await Promise.all([
this.loadScript('https://unpkg.com/tabulator-tables@4.9.3/dist/js/tabulator.min.js', 'Tabulator'),
this.loadStylesheet('https://unpkg.com/tabulator-tables@4.9.3/dist/css/tabulator.min.css')
]);
console.log('DataGridExtension loaded.');
return true;
}

unload() {
super.unload();
console.log('DataGridExtension unloaded.');
return true;
}
}

Autodesk.Viewing.theExtensionManager.registerExtension('DataGridExtension', DataGridExtension);

This time we're also loading multiple dependencies that are required by the Tabulator library.

Now let's import the JavaScript file to our application, and pass the extension ID to the viewer constructor:

import './extensions/LoggerExtension.js';
import './extensions/SummaryExtension.js';
import './extensions/HistogramExtension.js';
import './extensions/DataGridExtension.js';
const config = {
extensions: [
'LoggerExtension',
'SummaryExtension',
'HistogramExtension',
'DataGridExtension',
]
};
const viewer = new Autodesk.Viewing.GuiViewer3D(container, config);

Toolbar

Next, let's update the DataGridExtension class so that it adds a new button to the viewer toolbar when the extension gets loaded:

wwwroot/extensions/DataGridExtension.js
import { BaseExtension } from './BaseExtension.js';

class DataGridExtension extends BaseExtension {
constructor(viewer, options) {
super(viewer, options);
this._button = null;
}

async load() {
super.load();
await Promise.all([
this.loadScript('https://unpkg.com/tabulator-tables@4.9.3/dist/js/tabulator.min.js', 'Tabulator'),
this.loadStylesheet('https://unpkg.com/tabulator-tables@4.9.3/dist/css/tabulator.min.css')
]);
console.log('DataGridExtension loaded.');
return true;
}

unload() {
super.unload();
if (this._button) {
this.removeToolbarButton(this._button);
this._button = null;
}
console.log('DataGridExtension unloaded.');
return true;
}

onToolbarCreated() {
this._button = this.createToolbarButton('dashboard-datagrid-button', 'https://img.icons8.com/small/32/activity-grid.png', 'Show Data Grid');
this._button.onClick = () => {
// TODO
};
}
}

Autodesk.Viewing.theExtensionManager.registerExtension('DataGridExtension', DataGridExtension);

Data grid

Now let's create a custom docking panel that will host the actual Tabulator grid. Create a DataGridPanel.js file in the same folder where DataGridExtension.js is located, and add the following code to it:

wwwroot/extensions/DataGridPanel.js
const DATAGRID_CONFIG = {
requiredProps: ['name', 'Volume', 'Level'], // Which properties should be requested for each object
columns: [ // Definition of individual grid columns (see http://tabulator.info for more details)
{ title: 'ID', field: 'dbid' },
{ title: 'Name', field: 'name', width: 150 },
{ title: 'Volume', field: 'volume', hozAlign: 'left', formatter: 'progress' },
{ title: 'Level', field: 'level' }
],
groupBy: 'level', // Optional column to group by
createRow: (dbid, name, props) => { // Function generating grid rows based on recieved object properties
const volume = props.find(p => p.displayName === 'Volume')?.displayValue;
const level = props.find(p => p.displayName === 'Level' && p.displayCategory === 'Constraints')?.displayValue;
return { dbid, name, volume, level };
},
onRowClick: (row, viewer) => {
viewer.isolate([row.dbid]);
viewer.fitToView([row.dbid]);
}
};

export class DataGridPanel extends Autodesk.Viewing.UI.DockingPanel {
constructor(extension, id, title, options) {
super(extension.viewer.container, id, title, options);
this.extension = extension;
this.container.style.left = (options.x || 0) + 'px';
this.container.style.top = (options.y || 0) + 'px';
this.container.style.width = (options.width || 500) + 'px';
this.container.style.height = (options.height || 400) + 'px';
this.container.style.resize = 'none';
}

initialize() {
this.title = this.createTitleBar(this.titleLabel || this.container.id);
this.initializeMoveHandlers(this.title);
this.container.appendChild(this.title);
this.content = document.createElement('div');
this.content.style.height = '350px';
this.content.style.backgroundColor = 'white';
this.content.innerHTML = `<div class="datagrid-container" style="position: relative; height: 350px;"></div>`;
this.container.appendChild(this.content);
// See http://tabulator.info
this.table = new Tabulator('.datagrid-container', {
height: '100%',
layout: 'fitColumns',
columns: DATAGRID_CONFIG.columns,
groupBy: DATAGRID_CONFIG.groupBy,
rowClick: (e, row) => DATAGRID_CONFIG.onRowClick(row.getData(), this.extension.viewer)
});
}

update(model, dbids) {
model.getBulkProperties(dbids, { propFilter: DATAGRID_CONFIG.requiredProps }, (results) => {
this.table.replaceData(results.map((result) => DATAGRID_CONFIG.createRow(result.dbId, result.name, result.properties)));
}, (err) => {
console.error(err);
});
}
}
info

To keep things simple, the DataGridPanel class is currently hard-coding the grid columns to only show a couple of specific properties of our design metadata (specifically the object ID, object name, and Volume and Level properties). Depending on the type of design you will be loading into your application, you may want to change these.

Finally, add the new panel to our extension:

wwwroot/extensions/DataGridExtension.js
import { BaseExtension } from './BaseExtension.js';
import { DataGridPanel } from './DataGridPanel.js';

class DataGridExtension extends BaseExtension {
constructor(viewer, options) {
super(viewer, options);
this._button = null;
this._panel = null;
}

async load() {
super.load();
await Promise.all([
this.loadScript('https://unpkg.com/tabulator-tables@4.9.3/dist/js/tabulator.min.js', 'Tabulator'),
this.loadStylesheet('https://unpkg.com/tabulator-tables@4.9.3/dist/css/tabulator.min.css')
]);
console.log('DataGridExtension loaded.');
return true;
}

unload() {
super.unload();
if (this._button) {
this.removeToolbarButton(this._button);
this._button = null;
}
if (this._panel) {
this._panel.setVisible(false);
this._panel.uninitialize();
this._panel = null;
}
console.log('DataGridExtension unloaded.');
return true;
}

onToolbarCreated() {
this._panel = new DataGridPanel(this, 'dashboard-datagrid-panel', 'Data Grid', { x: 10, y: 10 });
this._button = this.createToolbarButton('dashboard-datagrid-button', 'https://img.icons8.com/small/32/activity-grid.png', 'Show Data Grid');
this._button.onClick = () => {
this._panel.setVisible(!this._panel.isVisible());
this._button.setState(this._panel.isVisible() ? Autodesk.Viewing.UI.Button.State.ACTIVE : Autodesk.Viewing.UI.Button.State.INACTIVE);
if (this._panel.isVisible() && this.viewer.model) {
this.update();
}
};
}

onModelLoaded(model) {
super.onModelLoaded(model);
if (this._panel && this._panel.isVisible()) {
this.update();
}
}

async update() {
const dbids = await this.findLeafNodes(this.viewer.model);
this._panel.update(this.viewer.model, dbids);
}
}

Autodesk.Viewing.theExtensionManager.registerExtension('DataGridExtension', DataGridExtension);

Try it out

Click on the new toolbar button to bring up the datagrid panel. The grid should list selected properties of all design elements, and clicking on any row in the grid should isolate the specific design element in the viewer.

Grid Result