Skip to content

Conversation

@protobi-pieter
Copy link

Problem

The getTable().addRow() workflow fails when working with tables loaded from Excel files, throwing:

Cannot read properties of undefined (reading 'length')

This prevents a common use case: loading an Excel template with existing tables and adding rows to them programmatically.

Additionally, even when the error doesn't occur:

  • Table references don't expand dynamically when rows are added
  • Excel filter buttons disappear after save/load cycle
  • Missing worksheet references cause inconsistent behavior

Solution

This PR fixes the table loading and addRow workflow with comprehensive changes to lib/doc/table.js and lib/doc/worksheet.js.

Key Changes

lib/doc/table.js:

  • Fixed autoFilterRef to target header row only (e.g., A1:C1) instead of all filter rows
  • Added _updateTableRef() method to dynamically update table ranges when rows change
  • Modified addRow() to update table references and call commit() to re-render properly
  • Modified removeRows() to update table references after removal
  • Added _writeRowToWorksheet() helper for targeted row writes
  • Added autoFilterRef getter/setter

lib/doc/worksheet.js:

  • Map tableRef to ref for Excel format compatibility when loading
  • Add empty rows array for loaded tables (Excel doesn't store row data in definitions)
  • Auto-detect headerRow when columns have names
  • Enable filterButton: true on columns when autoFilterRef exists

test/test-table-addrow.js:

  • Comprehensive test suite for the getTable().addRow() workflow
  • Tests both creating new tables and loading existing tables
  • Verifies table references update correctly
  • Verifies filter buttons are preserved

Example Usage

// Common template-based workflow - now works!
const Excel = require('exceljs');
const workbook = new Excel.Workbook();
await workbook.xlsx.readFile('template.xlsx');

const worksheet = workbook.getWorksheet('Data');
const table = worksheet.getTable('MyTable');

// Add rows to the loaded table
table.addRow(['Alice', 30, 'alice@example.com']);
table.addRow(['Bob', 25, 'bob@example.com']);

// Save with updated table
await workbook.xlsx.writeFile('output.xlsx');
// Filter buttons preserved ✅
// Table reference expanded correctly ✅

Test Results

$ node test/test-table-addrow.js /tmp/test.xlsx
Initial table created with 3 rows
Table retrieved: MyTable
Initial table ref: A1
Initial table rows: 3
Initial autoFilterRef: A1:C1

Adding new row with addRow()...
After addRow:
Table ref: A1:C5          # ✅ Expanded correctly
Table rows: 4
AutoFilterRef: A1:C1      # ✅ Header row only

=== Test 2: Load and modify table ===
Workbook loaded successfully
Loaded table: LoadTestTable
Adding row to loaded table...
After addRow to loaded table:
Table ref: B2:D3          # ✅ Expanded correctly
AutoFilterRef: B2:D2      # ✅ Preserved

SUCCESS: All table addRow tests passed!

Credits

Original implementation by Ryan Martin (@rmartin93)

This PR adopts and submits Ryan's excellent work to upstream with comprehensive testing.

Related Issues

Checklist

  • Code changes implement the fix
  • Comprehensive test coverage added
  • Tests pass locally
  • Existing tests still pass
  • Original author credited

Impact

This fix enables:

  • ✅ Template-based Excel workflows (load + modify + save)
  • ✅ Dynamic table population from databases
  • ✅ Preserving Excel table features (filters, formatting)
  • ✅ Professional data reporting pipelines

This is a critical fix for enterprise/production use cases where Excel templates are common.

Fixes the getTable().addRow() workflow which previously failed when
working with tables loaded from Excel files. This enables template-based
workflows where users load existing workbooks and add rows to tables.

Fixes:
- 'Cannot read properties of undefined (reading length)' error when
  calling addRow() on tables loaded from files
- Missing worksheet references in loaded tables
- Table references not expanding dynamically when rows are added
- Excel filter buttons disappearing after save/load cycle

Changes:

lib/doc/table.js:
- Fixed autoFilterRef to target header row only (e.g., A1:C1 format)
  instead of including all header rows
- Added _updateTableRef() method to dynamically update table ranges
  when rows are added or removed
- Modified addRow() to update table references and call commit() to
  re-render the entire table properly
- Added _writeRowToWorksheet() helper for targeted row writes
- Modified removeRows() to update table references after removal
- Added autoFilterRef getter/setter for table customization

lib/doc/worksheet.js:
- Map tableRef to ref for Excel format compatibility when loading
- Add empty rows array for loaded tables (Excel doesn't store row data
  in table definitions)
- Auto-detect headerRow when columns have names
- Enable filterButton: true on columns when autoFilterRef exists

test/test-table-addrow.js:
- Comprehensive test for getTable().addRow() workflow
- Tests both creating new tables and loading existing tables
- Verifies table references update correctly as rows are added
- Verifies filter buttons are preserved through save/load cycle

This fix enables the common pattern:
```javascript
const workbook = new Excel.Workbook();
await workbook.xlsx.readFile('template.xlsx');
const table = workbook.getWorksheet('Data').getTable('MyTable');
table.addRow(['new', 'data', 'here']); // Now works!
await workbook.xlsx.writeFile('output.xlsx');
```

Original implementation: Ryan Martin (@rmartin93)
Source: https://github.com/rmartin93/exceljs-fork (commit 6b77cea)
Discussion: exceljs#2987

Co-Authored-By: Ryan Martin <ryan@meraki-digital.com>
protobi-pieter added a commit to protobi/exceljs that referenced this pull request Nov 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant