I am trying to add cells to the existing table using insert API provided by office js. Microsoft official doc gave sample code to insert in sheet which works fine.
Excel.run(function (context) {
var sheet = context.workbook.worksheets.getItem("Sample");
var range = sheet.getRange("B4:E4");
range.insert(Excel.InsertShiftDirection.down);
return context.sync();
}).catch(errorHandlerFunction);
But i was trying do same with table on the sheet. But it is not working.
table = ctx.workbook.tables.getItem(tableName);
let range = table.getRange();
range.insert(Excel.InsertShiftDirection.down);
Is there a way to achieve this?
You could use Excel.TableRow, the sample code:
Excel.run(function (ctx) {
var tables = ctx.workbook.tables;
var values = [["Sample", "Values", "For", "New", "Row"]];
var row = tables.getItem("Table1").rows.add(null, values);
row.load('index');
return ctx.sync().then(function() {
console.log(row.index);
});
}).catch(function(error) {
console.log("Error: " + error);
if (error instanceof OfficeExtension.Error) {
console.log("Debug info: " + JSON.stringify(error.debugInfo));
}
});
The document can be found at https://learn.microsoft.com/en-us/javascript/api/excel/excel.tablerowcollection?view=excel-js-preview#add-index--values-
Related
I am looking for a best way to uniquely identify/recognize a selected range within Office.js Excel workbook. So far I am using bindings to set a name for a range i.e. A1:A1. However, it is not clear how to check when user selects the above range if it is a part of the above binding in the workbook.
To set the binding i use the below code:
var myBindings = Office.context.document.bindings;
var myAddress = "Sheet1!A1:A1";
myBindings.addFromNamedItemAsync(myAddress, Office.BindingType.Matrix, { id: "myBind" }, function (asyncResult) {
asyncResult.value.getDataAsync(function (asyncResult2) {
console.log(asyncResult2.value);
});
});
You could use getIntersectionOrNullObject to detect whether your selection is in namedRanges or not. it can return address of the selectedRange in getIntersectionOrNullObject or it will return null object
Here is the sample code
Excel.run(function (ctx) {
const selectedRange = ctx.workbook.getSelectedRange();
var binding = ctx.workbook.bindings.getItemAt(0);
var range = binding.getRange();
var sheetName = "Sheet1";
var rangeAddress = "A1:F8"; // replace to binding range address
// var rangeAddress = range.address;
var range =
ctx.workbook.worksheets.getItem(sheetName).getRange(rangeAddress).getIntersectionOrNullObject(selectedRange);
range.load('address');
console.log("test");
return ctx.sync().then(function () {
console.log("test2");
console.log(range.address);
});
}).catch(function (error) {
console.log("Error: " + error);
if (error instanceof OfficeExtension.Error) {
console.log("Debug info: " + JSON.stringify(error.debugInfo));
}
});
I'm looking for a Javascript equivalent of the Excel VBA Intersect method. I need to find the active cell table. Basically everything I do with tables is based on which table the user currently has selected. IIRC, there is currently there is no way to do this directly. So what I'm trying to do is basically this:
Get the selected range (still not perfect, as I really only want the ActiveCell, not the Selection).
Get the worksheet.
Loop through all tables on the worksheet.
Check each table and see if the selected range is in the table range.
I've monkey'd around a bit, and this is what I currently have which doesn't work...
Excel.run(function(ctx) {
var Selection = ctx.workbook.getSelectedRange();
var Tables = ctx.workbook.tables;
var TableNames = ctx.workbook.tables.load("name");
for (var i = 0; i < TableNames.items.length; i++)
{
var Table = ctx.workbook.tables.getItem(TableNames.items[i].name);
Table.getRange().load('address');
var Intersect = Selection.getBoundingRect(Table.getRange().address);
if (Intersect != null) {return ctx.sync().then(function() {
TableNames.items[i].name;
})};
}
return ctx.sync();
}).catch(function(error) {
console.log(error);
if (error instanceof OfficeExtension.Error) {
console.log("Debug info: " + JSON.stringify(error.debugInfo));
}
});
This would be a major step forward for the API if it was native. ;)
Thanks,
Zack
There are several ways to check whether the current selection intersects with a table. This snippet demonstrates two of them.
Both of the examples below are written with TypeScript 2.1+'s async/await syntax. The second method is made significantly simpler through the use of "await", but both are possible with just regular promise-chaining as well.
The ExcelApi 1.4+ one is vastly more efficient, but it will only run on newer builds of Excel (on subscription-based, not on 2016 MSI/RTM). It does all of its intersection-checks simultaneously.
The ExcelApi 1.1 version is less efficient if you have hundreds of tables, or if you're running on Excel Online. It requires more roundtrips to the server, as it checks every table intersection one-by-one, and relies on a thrown error to inform it that there is no intersection found.
ExcelApi 1.4+ approach:
$('#check-intersection-preview').click(function() {
// Note: this function uses a "Preview" API ("range.getIntersectionOrNull"),
// which is only available on the Beta CDN right now, and is subject to change.
// Do not rely on this for production. Instead, use the alternate
// (albeit less neat) version.
Excel.run(async function(context) {
var selection = context.workbook.getSelectedRange();
var tables = context.workbook.tables.load("name");
await context.sync();
var intersections: { [email: string]: Excel.Range } = { };
tables.items.forEach((table) => {
intersections[table.name] = table.getRange().
getIntersectionOrNullObject(selection).load("address");
});
await context.sync();
var found = false;
for (var tableName in intersections) {
var rangeOrNull = intersections[tableName];
if (!rangeOrNull.isNullObject) {
found = true;
console.log(`Intersection found with table "${tableName}". ` +
`Intersection range: "${rangeOrNull.address}".`);
}
}
if (!found) {
console.log("Selection does not intersect any table");
}
}).catch(errorHandler);
});
ExcelApi 1.1 approach:
$('#check-intersection-prod').click(function() {
Excel.run(async function(context) {
var selection = context.workbook.getSelectedRange();
var tables = context.workbook.tables.load("name");
await context.sync();
var found = false;
for (var i = 0; i < tables.items.length; i++) {
try {
var table = tables.items[i];
var intersectionRange = table.getRange()
.getIntersection(selection).load("address");
await context.sync();
// If reached past the sync, it means that "getIntersection"
// did not throw an error, and so the intersection must be valid.
found = true;
console.log(`Intersection found with table "${table.name}". ` +
`Intersection range: "${intersectionRange.address}".`);
} catch (e) {
var isExpectedError = e instanceof OfficeExtension.Error &&
(<OfficeExtension.Error>e).code === Excel.ErrorCodes.itemNotFound;
if (!isExpectedError) {
throw e;
}
}
}
if (!found) {
console.log("Selection does not intersect any table");
}
}).catch(errorHandler);
});
Common errorHandler helper:
function errorHandler(error) {
console.log(error);
if (error instanceof OfficeExtension.Error) {
console.log("Debug info: " + JSON.stringify(error.debugInfo));
}
}
TRY IT LIVE: You can try the Excel 1.4+ approach live in literally five clicks in the new Script Lab (https://aka.ms/getscriptlab). Simply install the Script Lab add-in (free), then choose "Import" in the navigation menu, and use the following GIST URL: https://gist.github.com/Zlatkovsky/3ebdf5587cdc56d23b289fb6a5645030. See more info about importing snippets to Script Lab.
I have written the following code to implement custom conditional formatting using Office JS -
function customFormatting() {
// Run a batch operation against the Excel object model
Excel.run(function (ctx) {
// Create a proxy object for the active worksheet
var sheet = ctx.workbook.worksheets.getActiveWorksheet();
//Queue a command to write the sample data to the specified range
//in the worksheet and bold the header row
var range = sheet.getRange("A2:E9");
var conditionalFormat = range.conditionalFormats.add(Excel.ConditionalFormatType.custom);
conditionalFormat.custom.format.fill.color = "red";
conditionalFormat.custom.rule = {formula:">1001 && <5000"};
//Run the queued commands, and return a promise to indicate task completion
return ctx.sync();
})
.then(function () {
app.showNotification("Success");
console.log("Success!");
})
.catch(function (error) {
// Always be sure to catch any accumulated errors that bubble up from the Excel.run execution
app.showNotification("Error: " + error);
console.log("Error: " + error);
if (error instanceof OfficeExtension.Error) {
console.log("Debug info: " + JSON.stringify(error.debugInfo));
}
});
}
I am getting the following error while running the code -
Error: TypeError: Attempted to assign to readonly property.
(Revising my answer to describe 2 possible solutions, since I'm not clear on exactly which scenario matches what you're trying to achieve.)
Solution 1: Highlight cell when cell value meets criteria
In this first scenario, let's assume you have this table in the active worksheet, and your objective is to highlight the cell in column E of any row where the value in column E is between 1001 and 5000:
The following code uses conditional formatting to set fill color to yellow in column E when the cell value is between 1001 and 5000.
Excel.run(function (ctx) {
var sheet = ctx.workbook.worksheets.getActiveWorksheet();
var range = sheet.getRange("E2:E9");
var conditionalFormat = range.conditionalFormats.add(Excel.ConditionalFormatType.cellValue);
conditionalFormat.cellValue.format.fill.color = "yellow";
conditionalFormat.cellValue.rule = { formula1: "=1001", formula2: "=5000", operator: "Between" };
return ctx.sync()
.then(function () {
//app.showNotification("Success");
console.log("Success!");
})
})
.catch(function (error) {
//app.showNotification("Error: " + error);
console.log("Error: " + error);
if (error instanceof OfficeExtension.Error) {
console.log("Debug info: " + JSON.stringify(error.debugInfo));
}
});
After this code runs, the table looks like this:
Solution 2: Highlight entire row when a specific cell value in the row meets criteria
In this next scenario, let's assume you have this table in the active worksheet, and your objective is to highlight the entire row of data (columns A-E) whenever the value in column E of a row is between 1001 and 5000:
The following code uses conditional formatting to set fill color to yellow for the entire row of data (columns A-E), whenever the value in column E of a row is between 1001 and 5000.
Excel.run(function (ctx) {
var sheet = ctx.workbook.worksheets.getActiveWorksheet();
var range = sheet.getRange("A2:E9");
var conditionalFormat = range.conditionalFormats.add(Excel.ConditionalFormatType.custom);
conditionalFormat.custom.format.fill.color = "yellow";
conditionalFormat.custom.rule.formula = '=IF((AND($E2>1001, $E2<5000)),TRUE)';
return ctx.sync()
.then(function () {
//app.showNotification("Success");
console.log("Success!");
})
})
.catch(function (error) {
//app.showNotification("Error: " + error);
console.log("Error: " + error);
if (error instanceof OfficeExtension.Error) {
console.log("Debug info: " + JSON.stringify(error.debugInfo));
}
});
After this code runs, the table looks like this:
conditionalFormat.custom.rule is read only. This means that you can't create an object and assign it to conditionalFormat.custom.rule as your code is trying to do. Instead you have to assign values to each property of the rule. For example:
conditionalFormat.custom.rule.formula = '=IF(B8>INDIRECT("RC[-1]",0),TRUE)';
Note that the formula value has to be a valid Excel formula, not a JavaScript expression as you are using.
i'm trying to insert a table inside the content control. Here is my code:
function insertTable() {
Word.run(function (context) {
var range = context.document.getSelection();
var cc = range.insertContentControl();
cc.title = "My Table";
var values = [["Apple", "red", "round"], ["Banana", "yellow", "long"], ["Pear", "green", "oblong"]];
context.load(cc);
return context.sync().then(function () {
var table = cc.insertTable(3, 3, 'Start', values);
})
// Synchronize the document state by executing the queued commands,
// and return a promise to indicate task completion.
.then(context.sync);
})
.catch(function (error) {
console.log('Error: ' + JSON.stringify(error));
if (error instanceof OfficeExtension.Error) {
console.log('Debug info: ' + JSON.stringify(error.debugInfo));
}
});
}
But i got this error.
Error: {"name":"OfficeExtension.Error","code":"InvalidArgument","message":"InvalidArgument","traceMessages":[],"innerError":null,"debugInfo":{"code":"InvalidArgument","message":"InvalidArgument","errorLocation":""},"stack":"InvalidArgument: InvalidArgument\n at Anonymous function (https://appsforoffice.microsoft.com/lib/beta/hosted/word-win32-16.01.js:21:211625)\n at yi (https://appsforoffice.microsoft.com/lib/beta/hosted/word-win32-16.01.js:21:249536)\n at st (https://appsforoffice.microsoft.com/lib/beta/hosted/word-win32-16.01.js:21:249623)\n at d (https://appsforoffice.microsoft.com/lib/beta/hosted/word-win32-16.01.js:21:249443)\n at c (https://appsforoffice.microsoft.com/lib/beta/hosted/word-win32-16.01.js:21:248029)"}
I'm using the beta word api:
<script src="https://appsforoffice.microsoft.com/lib/beta/hosted/office.js" type="text/javascript"></script>
Because on api version 1.1 there is not the method insertTable. Any idea why it doesn't work? I've seen on the documentation that this method is available on api version 1.3, are they released?
Thanks
I was stuck with the same problem. It turns out you cannot insert a table in the middle of a paragraph (or in a paragraph that contains something else). When you first add a paragraph, and insert the table in this paragraph you get the desired effect. Please see the code below.
All credits belong to Cindy Meister
function placeTable() {
Word.run(function (context) {
var values = [["Apple"]];
var selectionRange = context.document.getSelection();
var paragraph = selectionRange.insertParagraph("", "Before");
return context.sync()
.then(function () {
var table = paragraph.insertTable(1, 1, "Before", values);
var contentControl = table.insertContentControl();
})
.then(context.sync)
.catch(function (error) {
console.log(error);
});
});
I came here because I got the same error.
However, in my case it was because I provided the insertTable method a column value of less than what the values parameter had.
i.e. my values had three columns and I specified 2 in insertTable.
Hope this helps someone in the future.
How can I get the position of two worksheets using the Excel Javascript API?
Here is how it works just for one sheet:
Excel.run(function (ctx) {
var wSheetName = 'Sheet1';
var worksheet = ctx.workbook.worksheets.getItem(wSheetName);
worksheet.load('position')
return ctx.sync().then(function () {
console.log(worksheet.position);
});
});
=> it logs 0 to the console
But it doesn't logs anything if I try to get the position for two worksheets:
Excel.run(function (ctx) {
var wSheetName = 'Sheet1';
var wSheetName2 = 'Evars';
var worksheet = ctx.workbook.worksheets.getItem(wSheetName);
var worksheet2 = ctx.workbook.worksheets.getItem(wSheetName2);
worksheet.load('position')
worksheet2.load('position')
return ctx.sync().then(function () {
console.log(worksheet.position);
console.log(worksheet2.position);
});
});
I just tried your code, and it works fine. I wonder if you simply didn't have a sheet by one of those names, and so it was throwing an exception -- which was appearing to you as silent, since you didn't have a catch handler.
The code below, essentially the same as yours but with a catch statement, works correctly:
Excel.run(function(ctx) {
var wSheetName = 'Sheet1';
var wSheetName2 = 'Sheet2';
var worksheet = ctx.workbook.worksheets.getItem(wSheetName);
var worksheet2 = ctx.workbook.worksheets.getItem(wSheetName2);
worksheet.load('name, position')
worksheet2.load('name, position')
return ctx.sync().then(function () {
console.log(worksheet.name + ": " + worksheet.position);
console.log(worksheet2.name + ": " + worksheet2.position);
});
}).catch(function(error) {
OfficeHelpers.UI.notify(error);
OfficeHelpers.Utilities.log(error);
})
You can try this snippet live in literally five clicks in the new Script Lab (https://aka.ms/getscriptlab). Simply install the Script Lab add-in (free), then choose "Import" in the navigation menu, and use the following GIST URL: https://gist.github.com/Zlatkovsky/c61594f1c86970e8dba91fe94b7ca4b6. See more info about importing snippets to Script Lab.
Found the solution here ... maybe this will help someone
Excel.run(function (ctx) {
var worksheets = ctx.workbook.worksheets;
worksheets.load('items');
return ctx.sync().then(function () {
for (var i = 0; i < worksheets.items.length; i++) {
var sheet_name = worksheets.items[i].name;
var sheet_position = worksheets.items[i].position;
}
});