Skip to content

Commit

Permalink
custom header row index
Browse files Browse the repository at this point in the history
  • Loading branch information
Theo Ephraim committed Nov 7, 2021
1 parent 8c0932c commit 198e9c3
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 16 deletions.
19 changes: 13 additions & 6 deletions docs/classes/google-spreadsheet-worksheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,26 @@ The row-based interface is provided as a simplified way to deal with sheets that

Also note that the row-based API and cell-based API are isolated from each other, meaning when you load a set of rows, the corresponding cells are not loaded as well. You usually want to use one or the other.

#### `loadHeaderRow()` (async) :id=fn-loadHeaderRow
> Loads the header row (first row) of the sheet
_usually do not need to call this directly_
#### `loadHeaderRow(headerRowIndex)` (async) :id=fn-loadHeaderRow
> Loads the header row (usually first) of the sheet
Usually this is called automatically when loading rows via `getRows()` if the header row has not yet been loaded. However you should call this explicitly if you want to load a header row that is not the first row of the sheet.

Param|Type|Required|Description
---|---|---|---
`headerRowIndex`|Number|-|Optionally set custom header row index, if headers are not in first row<br>NOTE - not zero-indexed

-**Side effects** - `sheet.headerValues` is populated

#### `setHeaderRow(headerValues)` (async) :id=fn-setHeaderRow
> Set the header row (first row) of the sheet
#### `setHeaderRow(headerValues, headerRowIndex)` (async) :id=fn-setHeaderRow
> Set the header row (usually first) of the sheet
Param|Type|Required|Description
---|---|---|---
`headerValues`|[String]|✅|Array of strings to set as cell values in first row
`headerRowIndex`|Number|-|Optionally set custom header row index, if headers are not in first row<br>NOTE - not zero-indexed

-**Side effects** - first row of the sheet is filled, `sheet.headerValues` is populated
-**Side effects** - header row of the sheet is filled, `sheet.headerValues` is populated

#### `addRow(rowValues, options)` (async) :id=fn-addRow
> Append a new row to the sheet
Expand Down
1 change: 1 addition & 0 deletions docs/classes/google-spreadsheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ Param|Type|Required|Description
`props`|Object|-|Object of all sheet properties
`props.sheetId`|Number<br>_positive int_|-|Sheet ID, cannot be chagned after setting<br>_easiest to just let google handle it_
`props.headerValues`|[String]|-|Sets the contents of the first row, to be used in row-based interactions
`props.headerRowIndex`|Number|-|Set custom header row index (defaults to 1)
`props.[more]`|...|-|_See [GoogleSpreadsheetWorksheet](classes/google-spreadsheet-worksheet#basic-document-properties) for more props_


Expand Down
7 changes: 4 additions & 3 deletions lib/GoogleSpreadsheet.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,15 +299,16 @@ class GoogleSpreadsheet {
// https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddSheetRequest

const response = await this._makeSingleUpdateRequest('addSheet', {
properties: _.omit(properties, 'headers', 'headerValues'),
properties: _.omit(properties, 'headers', 'headerValues', 'headerRowIndex'),
});
// _makeSingleUpdateRequest already adds the sheet
const newSheetId = response.properties.sheetId;
const newSheet = this.sheetsById[newSheetId];

// allow it to work with `.headers` but `.headerValues` is the real prop
if (properties.headerValues || properties.headers) {
await newSheet.setHeaderRow(properties.headerValues || properties.headers);
const headers = properties.headerValues || properties.headers;
if (headers) {
await newSheet.setHeaderRow(headers, properties.headerRowIndex);
}

return newSheet;
Expand Down
20 changes: 13 additions & 7 deletions lib/GoogleSpreadsheetWorksheet.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class GoogleSpreadsheetWorksheet {
constructor(parentSpreadsheet, { properties, data }) {
this._spreadsheet = parentSpreadsheet; // the parent GoogleSpreadsheet instance

this._headerRowIndex = 1; // assume "header row" (for row-based calls) is in first row

// basic properties
this._rawProperties = properties;

Expand Down Expand Up @@ -51,6 +53,7 @@ class GoogleSpreadsheetWorksheet {
resetLocalCache(dataOnly) {
if (!dataOnly) this._rawProperties = null;
this.headerValues = null;
this._headerRowIndex = 1;
this._cells = [];
}

Expand Down Expand Up @@ -284,8 +287,9 @@ class GoogleSpreadsheetWorksheet {

// ROW BASED FUNCTIONS ///////////////////////////////////////////////////////////////////////////

async loadHeaderRow() {
const rows = await this.getCellsInRange(`A1:${this.lastColumnLetter}1`);
async loadHeaderRow(headerRowIndex) {
if (headerRowIndex !== undefined) this._headerRowIndex = headerRowIndex;
const rows = await this.getCellsInRange(`A${this._headerRowIndex}:${this.lastColumnLetter}${this._headerRowIndex}`);
if (!rows) {
throw new Error('No values in the header row - fill the first row with header values before trying to interact with rows');
}
Expand All @@ -296,7 +300,7 @@ class GoogleSpreadsheetWorksheet {
checkForDuplicateHeaders(this.headerValues);
}

async setHeaderRow(headerValues) {
async setHeaderRow(headerValues, headerRowIndex) {
if (!headerValues) return;
if (headerValues.length > this.columnCount) {
throw new Error(`Sheet is not large enough to fit ${headerValues.length} columns. Resize the sheet first.`);
Expand All @@ -308,15 +312,17 @@ class GoogleSpreadsheetWorksheet {
throw new Error('All your header cells are blank -');
}

if (headerRowIndex) this._headerRowIndex = headerRowIndex;

const response = await this._spreadsheet.axios.request({
method: 'put',
url: `/values/${this.encodedA1SheetName}!1:1`,
url: `/values/${this.encodedA1SheetName}!${this._headerRowIndex}:${this._headerRowIndex}`,
params: {
valueInputOption: 'USER_ENTERED', // other option is RAW
includeValuesInResponse: true,
},
data: {
range: `${this.a1SheetName}!1:1`,
range: `${this.a1SheetName}!${this._headerRowIndex}:${this._headerRowIndex}`,
majorDimension: 'ROWS',
values: [[
...trimmedHeaderValues,
Expand Down Expand Up @@ -367,7 +373,7 @@ class GoogleSpreadsheetWorksheet {

const response = await this._spreadsheet.axios.request({
method: 'post',
url: `/values/${this.encodedA1SheetName}!A1:append`,
url: `/values/${this.encodedA1SheetName}!A${this._headerRowIndex}:append`,
params: {
valueInputOption: options.raw ? 'RAW' : 'USER_ENTERED',
insertDataOption: options.insert ? 'INSERT_ROWS' : 'OVERWRITE',
Expand Down Expand Up @@ -421,7 +427,7 @@ class GoogleSpreadsheetWorksheet {

if (!this.headerValues) await this.loadHeaderRow();

const firstRow = 2 + options.offset; // skip first row AND not zero indexed
const firstRow = 1 + this._headerRowIndex + options.offset;
const lastRow = firstRow + options.limit - 1; // inclusive so we subtract 1
const lastColumn = columnToLetter(this.headerValues.length);
const rawRows = await this.getCellsInRange(
Expand Down

0 comments on commit 198e9c3

Please sign in to comment.