Identify Active Chart and Selected Chart Element in Office-JS - excel

I cobbled together some JavaScript to label the last point of each series in a chart (see below). This is a simplified version of a much-used function in a VBA add-in.
I used let mychart = mysheet.charts.getItemAt(0); to specify that the code should run on the first chart object on the worksheet. It would be more useful to run the code on the chart selected by the user.
How do I identify which chart the user has selected (ActiveChart in VBA-speak)?
Similarly I used for (var iseries = 0; iseries < nseries; iseries++) to run the code on all series in the chart. It would be more useful to run the code on the specific series selected by the user.
How do I identify which series in the chart has been selected by the user (related to TypeName(Selection) in VBA)?
Here is my Office-JS code:
$("#run").click(() => tryCatch(labelLastPoint));
//$("#label-last-point").click(labelLastPoint);
async function labelLastPoint() {
await Excel.run(async (context) => {
let mysheet = context.workbook.worksheets.getActiveWorksheet();
let mychart = mysheet.charts.getItemAt(0);
let seriescollection = mychart.series;
seriescollection.load("count");
await context.sync();
console.log("Number of Series: " + seriescollection.count);
let nseries = seriescollection.count;
for (var iseries = 0; iseries < nseries; iseries++) {
console.log("- Series Number " + iseries);
let myseries = seriescollection.getItemAt(iseries);
let pointcollection = myseries.points;
pointcollection.load("count");
await context.sync();
let npoints = myseries.points.count;
let mypoint = myseries.points.getItemAt(npoints - 1);
mypoint.hasDataLabel = true;
mypoint.dataLabel.showSeriesName = true;
mypoint.dataLabel.showValue = false;
}
});
}

As I understand it you're looking for API methods that might be called "getSelectedChart" and "getSelectedSeries". I'm afraid that there are no APIs in Office JS that do that. But it is a good idea. Please suggest that at Office Developer User Voice.
There is a getSelectedDataAsync method in the Shared Office JS APIs, but it does not support charts or series.
In the meantime, would it be possible in your scenario to have a drop down in the task pane, in which the user can specify, by title, the chart/series that is of interest?

Related

Excel Office Script not clearing cells. Getting unreachable code detected (7027) error

I have this script which works all except for the clearing of the B4:B120 area "// Clear the "Margin Updates" column." section which is greyed out for some reason):
function main(workbook: ExcelScript.Workbook): ReportImages {
// Recalculate the workbook to ensure all tables and charts are updated.
workbook.getApplication().calculate(ExcelScript.CalculationType.full);
// Get the data from the "Target Margins - FHM" table. (name of Excel tab, not name of table)
let sheet1 = workbook.getWorksheet("Sheet1");
const table = workbook.getWorksheet('Target Margins - FHM').getTables()[0];
const rows = table.getRange().getTexts();
// Get only the Product Type and "Margin Update" columns, then remove the "Total" row.
const selectColumns = rows.map((row) => {
return [row[0], row[1]];
});
// Delete the "ChartSheet" worksheet if it's present, then recreate it.
workbook.getWorksheet('ChartSheet')?.delete();
const chartSheet = workbook.addWorksheet('ChartSheet');
// Add the selected data to the new worksheet.
const targetRange = chartSheet.getRange('A1').getResizedRange(selectColumns.length - 1, selectColumns[0].length - 1);
targetRange.setValues(selectColumns);
// Get images of the chart and table, then return them for a Power Automate flow.
const tableImage = table.getRange().getImage();
return { tableImage };
// Clear the "Margin Updates" column.
const targetSheet = workbook.getActiveWorksheet();
const getRange = targetSheet.getRange("B4:B120");
getRange.clear(ExcelScript.ClearApplyTo.contents);`
}
// The interface for table and chart images.
interface ReportImages {
tableImage: string
}
The code copies the data in sections of the A and B columns (which constitute a table) and sends an email via Power Automate flow. Unfortunately, I need the section of the B column to be clear of values (not formatting or style) after which this flow is not doing.
I'd greatly appreciate help with this problem.
Thank you.
#cybernetic. nomad:
When I try using Range ("B4:B120").Clear I receive
unreachable code detected (7027)
and
and "cannot find name 'Range' (2304)
Office Script Range Clear Error
In JavaScript, the function exits as soon as the return keyword is evaluated. That's why it's saying your code is unreachable. So you have to restructure your code so that the return happens at the end. So you can update your code to look something like this:
// Clear the "Margin Updates" column.
const targetSheet = workbook.getActiveWorksheet();
const getRange = targetSheet.getRange("B4:B120");
getRange.clear(ExcelScript.ClearApplyTo.contents);
return { tableImage };

Getting an Image of a Power Pivot including Conditional formatting and send email

I'm trying to use the combination of Excel Office Script and Power Automate to send email with an image of Pivot Table.
Below is the code I came up with, but the resulting image that gets sent doesn't include the conditional formatting, only the data and the standard formatting get sent.
I even tried to recreate the conditional formatting within the script code, but no success.
Any ideas? Thanks!
function main(workbook: ExcelScript.Workbook): BudImg {
//Select Budget table
let selection = workbook.getWorksheet("Overview").getRange("A45:R59")
// Add a new worksheet
let sheet1 = workbook.addWorksheet("ScreenShotSheet");
//Paste to range A1 on sheet2 from range A20:J37 on selectedSheet
sheet1.getRange("A45").copyFrom(selection, ExcelScript.RangeCopyType.values, false, false);
sheet1.getRange("A45").copyFrom(selection, ExcelScript.RangeCopyType.formats, false, false);
//adjust columns
//sheet1.getRange("A:R").getFormat().autofitColumns();
//re-create conditional formatting
let conditionalFormatting: ExcelScript.ConditionalFormat;
conditionalFormatting = sheet1.getRange("K:R").addConditionalFormat(ExcelScript.ConditionalFormatType.cellValue);
conditionalFormatting.getCellValue().getFormat().getFont().setColor("#9C0006");
conditionalFormatting.getCellValue().getFormat().getFill().setColor("#FFC7CE");
conditionalFormatting.getCellValue().setRule({ formula1: "=0", formula2: undefined, operator: ExcelScript.ConditionalCellValueOperator.lessThan, });
//take screenshot
let table = sheet1.getRange("A45:R59");
let tableImg = selection.getImage();
//delete screenshotsheet
workbook.getWorksheet('ScreenShotSheet').delete();
return {tableImg};
}
interface BudImg {
tableImg: string
}
'''
To get an image of a pivot table, you need to have a line like the below:
workbook.getWorksheet("Sheet1").getPivotTable("My Pivot Table").getLayout().getRange().getImage();
Basically, you can specify the pivot table that you want using getPivotTable(id) and then you need to get the layout and the range of that layout. Then finally, you can use the getImage method. Hope that helps!
Your conditional formatting rule highlights the values which are equal to zero. You can just loop through the values of the range (K:R), see if they're zero, and if so, set the cells to the color you used in the conditional formatting. If you do it this way, the colors should be maintained when you create an image. You can see code to do that below:
function main(workbook: ExcelScript.Workbook) {
let sh: ExcelScript.Worksheet = workbook.getWorksheet("Sheet1")
let range: ExcelScript.Range = sh.getRange("K:R")
let vals: string[][] = range.getValues() as string[][]
let rowCount:number = range.getRowCount()
let colCount:number = range.getColumnCount()
for (let i = 0; i < rowCount; i++){
for (let j = 0; j < colCount; j++){
if (vals[i][j] as unknown === 0) {
let rang: ExcelScript.Range = sh.getRangeByIndexes(i,j,1,1)
rang.getFormat().getFont().setColor("#9C0006");
rang.getFormat().getFill().setColor("#FFC7CE");
}
}
}
}

Cannot find Excel in Javascript Chart API

I was trying to use this Microsoft tutorial Excel Chart Add-in - Javascript API
In the first example, it has the code
Excel.run(function (context) {
var sheet = context.workbook.worksheets.getItem("Sample");
var dataRange = sheet.getRange("A1:B13");
var chart = sheet.charts.add("Line", dataRange, "auto");
chart.title.text = "Sales Data";
chart.legend.position = "right"
chart.legend.format.fill.setSolidColor("white");
chart.dataLabels.format.font.size = 15;
chart.dataLabels.format.font.color = "black";
return context.sync();
}).catch(errorHandlerFunction);
If I run the code example I receive 2 errors. One that it cannot find excel from
Excel.run
And the errorhandler function is not defined, which appears to be correct.
Are these typos in new Microsoft documents? If not what have I got to change?
Version: excel 365 online build 16.0.13615.35052
2 things that you need to make sure you have in order to run this code succesfully.
Please add the errorHandlerFunction this could be be as easy as this:
function errorHandlerFunction(e ){
console.log("exception" + e );
}
Make sure you have a worksheet named "Sample". Make sure its exactly that name without trailing blank spaces.

Office JS "chart.legend.legendEntries.getItemAt(i).visible = true (or false) run well in Excel online but no in Excel desktop

This code works well in Excel online but not works in Excel desktop. In Excel Desktop it generate error in browser log :
"This operation is not permitted for the current object." on the line "chart.legend.legendEntries.getItemAt(i).visible".
Here the sample of the code where the problem occurs :
// Create an chart.
chart = activeSheet.charts.add(chartType, chartRange, 'Auto');
...
// Add a serie
for (let i = 0; i < nbSeries; ++i) {
const newSeries = chart.series.add(legend);
}
...
// Set legend visibility
for (let i = 0; i < nbSeries; i += 1) {
chart.legend.legendEntries.getItemAt(i).visible = false;
}
The legendEntry is available since ExcelApi 1.7.
My Excel is an version 1912.
Thank you for you help !
When you want to hide the legend, you could use chart.legend.visible = false;.
The difference between Excel desktop and Excel online is that for Excel desktop, the new created chart doesn't include Legend, which included by default for Excel online.
If we try to get chart.legend.legendEntries when there's no Legend in Chart, we will get this error message.

Get the fill color of a range of cells officejs

I'm new to office.js and making add ins and I'm trying to make an add in for Excel. I've run into an issue for one thing that seems like it should be very easy, but isn't. I'm just trying to get the background color of the selected cells. From what I can tell, I'll need to loop through each selected cell and check the fill.color value individually, which is fine, except I keep getting an error when trying to read this property.
Error PropertyNotLoaded: The property 'color' is not available. Before reading the property's value, call the load method on the containing object and call "context.sync()" on the associated request context.
I don't quite understand why I would have to run the context.sync() for this, when it's already being run and I'm trying to use the code that was already generated by Visual Studio for the add in.
The error is confusing because I'm able to set the color like this without any issues. Here is the code I've added trying to get the fill color. The first line is commented out, but adds an orange fill to the selected cells no problem. I only added this to see if I could read out a value I knew was already set. I'm trying to get the user defined fill for a selected range though. The second line is where the error gets thrown.
//sourceRange.getCell(i, j).format.fill.color = "orange"; // this sets the color no problem when uncommented
$('#fa-output').append("color: " + sourceRange.getCell(i,j).format.fill.color + "<br>"); //this is where it can't get the fill color
I'm using the example that Visual Studio generates where it will randomly generate 9 cells of random numbers and highlight the highest number in the selected range. Here is the full code for this method:
// Run a batch operation against the Excel object model
Excel.run(function (ctx) {
// Create a proxy object for the selected range and load its properties
var sourceRange = ctx.workbook.getSelectedRange().load("values, rowCount, columnCount, format");
// Run the queued-up command, and return a promise to indicate task completion
return ctx.sync()
.then(function () {
var highestRow = 0;
var highestCol = 0;
var highestValue = sourceRange.values[0][0];
// Find the cell to highlight
for (var i = 0; i < sourceRange.rowCount; i++) {
for (var j = 0; j < sourceRange.columnCount; j++) {
//sourceRange.getCell(i, j).format.fill.color = "orange"; // this sets the color no problem when uncommented
$('#fa-output').append("color: " + sourceRange.getCell(i,j).format.fill.color + "<br>"); //this is where it can't get the fill color
if (!isNaN(sourceRange.values[i][j]) && sourceRange.values[i][j] > highestValue) {
highestRow = i;
highestCol = j;
highestValue = sourceRange.values[i][j];
}
}
}
cellToHighlight = sourceRange.getCell(highestRow, highestCol);
sourceRange.worksheet.getUsedRange().format.font.bold = false;
// Highlight the cell
cellToHighlight.format.font.bold = true;
$('#fa-output').append("<br>The highest value is " + highestValue);
})
.then(ctx.sync);
})
.catch(errorHandler);
You have a lot of commented out code in your code that makes it hard to read.
At any rate, this is expected behavior. You have to load() and then sync() when you want to read a property of an object in the workbook. It's the load-and-sync that brings the value of the property from the workbook to the JavaScript in your add-in so you can read it. Your code is trying to read a property that it hasn't first loaded. The following is a simple example:
const cell = context.workbook.getActiveCell();
cell.load('format/fill/color');
await context.sync();
console.log(cell.format.fill.color);
ES5 version:
const cell = context.workbook.getActiveCell();
cell.load('format/fill/color');
return context.sync()
.then(function () {
console.log(cell.format.fill.color);
});
You should also take a look at the Range.getCellProperties() method, which is a kind of wrapper around the load.

Resources