create-gojs-kit
Version:
A CLI for downloading GoJS samples, extensions, and docs
483 lines (439 loc) • 19 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
<meta name="description" content="Interactively adding and removing rows and columns of a Table Panel in a GoJS Node." />
<meta itemprop="description" content="Interactively adding and removing rows and columns of a Table Panel in a GoJS Node." />
<meta property="og:description" content="Interactively adding and removing rows and columns of a Table Panel in a GoJS Node." />
<meta name="twitter:description" content="Interactively adding and removing rows and columns of a Table Panel in a GoJS Node." />
<link rel="preconnect" href="https://rsms.me/">
<link rel="stylesheet" href="../assets/css/style.css">
<!-- Copyright 1998-2025 by Northwoods Software Corporation. -->
<meta itemprop="name" content="Add/Remove/Swap Columns in Table Panels" />
<meta property="og:title" content="Add/Remove/Swap Columns in Table Panels" />
<meta name="twitter:title" content="Add/Remove/Swap Columns in Table Panels" />
<meta property="og:image" content="https://gojs.net/latest/assets/images/screenshots/addremovecolumns.png" />
<meta itemprop="image" content="https://gojs.net/latest/assets/images/screenshots/addremovecolumns.png" />
<meta name="twitter:image" content="https://gojs.net/latest/assets/images/screenshots/addremovecolumns.png" />
<meta property="og:url" content="https://gojs.net/latest/samples/addRemoveColumns.html" />
<meta property="twitter:url" content="https://gojs.net/latest/samples/addRemoveColumns.html" />
<meta name="twitter:card" content="summary_large_image" />
<meta property="og:type" content="website" />
<meta property="twitter:domain" content="gojs.net" />
<title>
Add/Remove/Swap Columns in Table Panels | GoJS Diagramming Library
</title>
</head>
<body>
<!-- This top nav is not part of the sample code -->
<nav id="navTop" class=" w-full h-[var(--topnav-h)] z-30 bg-white border-b border-b-gray-200">
<div class="max-w-screen-xl mx-auto flex flex-wrap items-start justify-between px-4">
<a class="text-white bg-nwoods-primary font-bold !leading-[calc(var(--topnav-h)_-_1px)] my-0 px-2 text-4xl lg:text-5xl logo"
href="../">
GoJS
</a>
<div class="relative">
<button id="topnavButton" class="h-[calc(var(--topnav-h)_-_1px)] px-2 m-0 text-gray-900 bg-inherit shadow-none md:hidden hover:!bg-inherit hover:!text-nwoods-accent hover:!shadow-none" aria-label="Navigation">
<svg class="h-7 w-7 block" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<div id="topnavList" class="hidden md:block">
<div class="absolute right-0 z-30 flex flex-col items-end rounded border border-gray-200 p-4 pl-12 shadow bg-white text-gray-900 font-semibold
md:flex-row md:space-x-4 md:items-start md:border-0 md:p-0 md:shadow-none md:bg-inherit">
<a href="../learn/">Learn</a>
<a href="../samples/">Samples</a>
<a href="../intro/">Intro</a>
<a href="../api/">API</a>
<a href="../download.html">Download</a>
<a href="https://forum.nwoods.com/c/gojs/11" target="_blank" rel="noopener">Forum</a>
<a id="tc" href="https://nwoods.com/contact.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/contact.html', 'contact');">Contact</a>
<a id="tb" href="https://nwoods.com/sales/index.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/sales/index.html', 'buy');">Buy</a>
</div>
</div>
</div>
</div>
</nav>
<script>
window.addEventListener("DOMContentLoaded", function () {
// topnav
var topButton = document.getElementById("topnavButton");
var topnavList = document.getElementById("topnavList");
if (topButton && topnavList) {
topButton.addEventListener("click", function (e) {
topnavList
.classList
.toggle("hidden");
e.stopPropagation();
});
document.addEventListener("click", function (e) {
// if the clicked element isn't the list, close the list
if (!topnavList.classList.contains("hidden") && !e.target.closest("#topnavList")) {
topButton.click();
}
});
// set active <a> element
var url = window
.location
.href
.toLowerCase();
var aTags = topnavList.getElementsByTagName('a');
for (var i = 0; i < aTags.length; i++) {
var lowerhref = aTags[i]
.href
.toLowerCase();
if (lowerhref.endsWith('.html'))
lowerhref = lowerhref.slice(0, -5);
if (url.startsWith(lowerhref)) {
aTags[i]
.classList
.add('active');
break;
}
}
}
});
</script>
<div class="flex flex-col prose">
<div class="w-full max-w-screen-xl mx-auto">
<!-- * * * * * * * * * * * * * -->
<!-- Start of GoJS sample code -->
<script src="https://cdn.jsdelivr.net/npm/gojs@3.1.0"></script>
<div id="allSampleContent" class="p-4 w-full">
<script id="code">
function tableRowItemTemplate() {
return (
new go.Panel({
// each of which as "attr" and "text" properties
stretch: go.Stretch.Fill,
alignment: go.Spot.TopLeft
})
.bind('column', 'attr', (a, elt) => {
// ELT is this bound item/cell Panel
// elt.data will be the cell object
// elt.panel.data will be the person/row data object
// elt.part.data will be the node data object
// "columnDefinitions" is on the node data object, so:
var cd = findColumnDefinitionForName(elt.part.data, a);
if (cd !== null) return cd.column;
throw new Error('unknown column name: ' + a);
})
// you could also have other Bindings here for this cell
.add(
new go.TextBlock({
editable: true,
margin: new go.Margin(2, 2, 0, 2),
wrap: go.Wrap.None
})
.bindTwoWay('text')
)
);
}
function init() {
myDiagram = new go.Diagram('myDiagramDiv', { 'undoManager.isEnabled': true });
myDiagram.nodeTemplate =
new go.Node('Auto')
.add(
new go.Shape({ fill: 'white' }),
new go.Panel('Table', {
margin: 0.5,
// the rows for the people
name: 'TABLE',
defaultAlignment: go.Spot.Left,
defaultColumnSeparatorStroke: 'black',
// bound to a person/row data object
itemTemplate:
new go.Panel('TableRow', { itemTemplate: tableRowItemTemplate() })
.bind('itemArray', 'columns')
})
.bind('itemArray', 'people')
.addRowDefinition(0, { background: 'lightgray' })
.addRowDefinition(1, { separatorStroke: 'black' })
// the table headers -- remains even if itemArray is empty
.add(
new go.Panel('TableRow', {
isPanelMain: true,
// bound to a column definition object
itemTemplate:
new go.Panel()
.bind('column')
.add(
new go.TextBlock({
margin: new go.Margin(2, 2, 0, 2),
font: 'bold 10pt sans-serif'
})
.bind('text')
)
})
.bind('itemArray', 'columnDefinitions')
)
);
myDiagram.model = new go.GraphLinksModel({
copiesArrays: true,
copiesArrayObjects: true,
nodeDataArray: [
{
// first node
key: 1,
columnDefinitions: [
// each column definition needs to specify the column used
{ attr: 'name', text: 'Name', column: 0 },
{ attr: 'phone', text: 'Phone #', column: 1 },
{ attr: 'office', text: 'Office', column: 2 }
],
people: [
// the table of people
// each row is a person with an Array of Objects associating a column name with a text value
{
columns: [
{ attr: 'name', text: 'Alice' },
{ attr: 'phone', text: '2345' },
{ attr: 'office', text: 'C4-E18' }
]
},
{
columns: [
{ attr: 'name', text: 'Bob' },
{ attr: 'phone', text: '9876' },
{ attr: 'office', text: 'E1-B34' }
]
},
{
columns: [
{ attr: 'name', text: 'Carol' },
{ attr: 'phone', text: '1111' },
{ attr: 'office', text: 'C4-E23' }
]
},
{
columns: [
{ attr: 'name', text: 'Ted' },
{ attr: 'phone', text: '2222' },
{ attr: 'office', text: 'C4-E197' }
]
}
]
},
{
// second node
key: 2,
columnDefinitions: [
{ attr: 'name', text: 'Name', column: 0 },
{ attr: 'phone', text: 'Phone #', column: 2 }, // note the different order of columns
{ attr: 'office', text: 'Office', column: 1 }
],
people: [
{
columns: [
{ attr: 'name', text: 'Robert' },
{ attr: 'phone', text: '5656' },
{ attr: 'office', text: 'B1-A27' }
]
},
{
columns: [
{ attr: 'name', text: 'Natalie' },
{ attr: 'phone', text: '5698' },
{ attr: 'office', text: 'B1-B6' }
]
}
]
}
],
linkDataArray: [{ from: 1, to: 2 }]
});
}
// Add or remove a person row from the selected node's table of people.
function insertIntoArray() {
var n = myDiagram.selection.first();
if (n === null) return;
var d = n.data;
myDiagram.startTransaction('insertIntoTable');
// add item as second in the list, at index #1
// of course this new data could be more realistic:
myDiagram.model.insertArrayItem(d.people, 1, {
columns: [
{ attr: 'name', text: 'Elena' },
{ attr: 'phone', text: '456' },
{ attr: 'office', text: 'LA' }
]
});
myDiagram.commitTransaction('insertIntoTable');
}
function removeFromArray() {
var n = myDiagram.selection.first();
if (n === null) return;
var d = n.data;
myDiagram.startTransaction('removeFromTable');
// remove second item of list, at index #1
if (d.people.length > 1) myDiagram.model.removeArrayItem(d.people, 1);
myDiagram.commitTransaction('removeFromTable');
}
// add or remove a column from the selected node's table of people
function findColumnDefinitionForName(nodedata, attrname) {
var columns = nodedata.columnDefinitions;
for (var i = 0; i < columns.length; i++) {
if (columns[i].attr === attrname) return columns[i];
}
return null;
}
function findColumnDefinitionForColumn(nodedata, idx) {
var columns = nodedata.columnDefinitions;
for (var i = 0; i < columns.length; i++) {
if (columns[i].column === idx) return columns[i];
}
return null;
}
function addColumn(attrname) {
var n = myDiagram.selection.first();
if (n === null) return;
var d = n.data;
// if name is not given, find an unused column name
if (attrname === undefined || attrname === '') {
attrname = 'new';
var count = 1;
while (findColumnDefinitionForName(d, attrname) !== null) {
attrname = 'new' + (count++).toString();
}
}
// find an unused column #
var col = 3;
while (findColumnDefinitionForColumn(d, col) !== null) {
col++;
}
myDiagram.startTransaction('addColumn');
var model = myDiagram.model;
// add a column definition for the node's whole table
model.addArrayItem(d.columnDefinitions, {
attr: attrname,
text: attrname,
column: col
});
// add cell to each person in the node's table of people
var people = d.people;
for (var j = 0; j < people.length; j++) {
var person = people[j];
model.addArrayItem(person.columns, {
attr: attrname,
text: Math.floor(Math.random() * 1000).toString()
});
}
myDiagram.commitTransaction('addColumn');
}
function removeColumn() {
var n = myDiagram.selection.first();
if (n === null) return;
var d = n.data;
var coldef = d.columnDefinitions[3]; // get the fourth column
if (coldef === undefined) return;
var attrname = coldef.attr;
myDiagram.startTransaction('removeColumn');
var model = myDiagram.model;
model.removeArrayItem(d.columnDefinitions, 3);
n.findObject('TABLE').removeColumnDefinition(coldef.column);
// update columns for each person in this table
var people = d.people;
for (var j = 0; j < people.length; j++) {
var person = people[j];
var columns = person.columns;
for (var k = 0; k < columns.length; k++) {
var cell = columns[k];
if (cell.attr === attrname) {
// get rid of this attribute cell from the person.columns Array
model.removeArrayItem(columns, k);
break;
}
}
}
myDiagram.commitTransaction('removeColumn');
}
function swapTwoColumns() {
myDiagram.startTransaction('swapColumns');
var model = myDiagram.model;
myDiagram.selection.each(n => {
if (!(n instanceof go.Node)) return;
var d = n.data;
var phonedef = findColumnDefinitionForName(d, 'phone');
if (phonedef === null) return;
var phonecolumn = phonedef.column; // remember the column number
var officedef = findColumnDefinitionForName(d, 'office');
if (officedef === null) return;
var officecolumn = officedef.column; // and this one too
model.setDataProperty(phonedef, 'column', officecolumn);
model.setDataProperty(officedef, 'column', phonecolumn);
model.updateTargetBindings(d); // update all bindings, to get the cells right
});
myDiagram.commitTransaction('swapColumns');
}
window.addEventListener('DOMContentLoaded', init);
</script>
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width: 100%; height: 400px"></div>
<p>Add a row or Remove the second row of the table held by the selected node:</p>
<button onclick="insertIntoArray()">Insert Into Array</button>
<button onclick="removeFromArray()">Remove From Array</button>
<p>Add a column or Remove the fourth column from the table of the selected node:</p>
<button onclick="addColumn()">Add Column</button>
<button onclick="removeColumn()">Remove Column</button>
<p>Swap the "phone" and "office" columns for each selected node:</p>
<button onclick="swapTwoColumns()">Swap Two Columns</button>
<br><br>
<p>
This sample shows how table panel row and columns can be manipulated using
HTML interactions. See also the
<a href="../samples/ColumnResizing.html">Column and Row Resizing Tools</a>.
</p>
</div>
</div>
<!-- * * * * * * * * * * * * * -->
<!-- End of GoJS sample code -->
</div>
<div id="allTagDescriptions" class="p-4 w-full max-w-screen-xl mx-auto">
<hr/>
<h3 class="text-xl">GoJS Features in this sample</h3>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>Table Panels</h4>
<p>
The "Table" Panel, <a href="../api/symbols/Panel.html#static-Table" target="api">Panel.Table</a>, arranges objects in rows and columns.
Each object in a Table Panel is put into the cell indexed by the value of <a href="../api/symbols/GraphObject.html#row" target="api">GraphObject.row</a> and <a href="../api/symbols/GraphObject.html#column" target="api">GraphObject.column</a>.
The panel will look at the rows and columns for all of the objects in the panel to determine how many rows and columns the table should have.
More information can be found in the <a href="../intro/tablePanels.html">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#tables">Related samples</a>
</p>
<hr>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>Item Arrays</h4>
<p>
It is sometimes useful to display a variable number of elements in a node by data binding to a JavaScript Array.
In GoJS, this is simply achieved by binding (or setting) <a href="../api/symbols/Panel.html#itemArray" target="api">Panel.itemArray</a>.
The <a href="../api/symbols/Panel.html" target="api">Panel</a> will create an element in the panel for each value in the Array.
More information can be found in the <a href="../intro/itemArrays.html">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#itemarrays">Related samples</a>
</p>
<hr>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>HTML Interaction</h4>
<p>
GoJS Diagrams can be used alongside other HTML elements in a webapp.
For custom Text Editors, Context Menus, and ToolTips, which are invoked and hidden via GoJS tool operations, it is best to use the <a href="../api/symbols/HTMLInfo.html" target="api">HTMLInfo</a> class.
</p>
<p>
More information can be found in the <a href="../intro/HTMLinteraction.html">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#html">Related samples</a>
</p>
<hr>
</div>
</div>
</body>
<!-- This script is part of the gojs.net website, and is not needed to run the sample -->
<script src="../assets/js/goSamples.js"></script>
</html>