Hiding columns using office scripts - office-scripts

I would like to hide certain columns using office script for Excel Online workbook. I used the recorder to create the script. But when I ran it, it resulted in errors. Below is the recorded script:
function main(workbook: ExcelScript.Workbook) {
let lockbox = workbook.getTable("Lockbox");
// Set visibility of column(s) at range D:P on lockbox to true
lockbox.getRange("D:P").setColumnHidden(true);
// Set visibility of column(s) at range R:V on lockbox to true
lockbox.getRange("R:V").setColumnHidden(true);
// Set visibility of column(s) at range AB:AB on lockbox to true
lockbox.getRange("AB:AB").setColumnHidden(true);
// Set visibility of column(s) at range AE:AO on lockbox to true
lockbox.getRange("AE:AO").setColumnHidden(true);
}
The Problems are below, but don't understand why it's not working:
[4, 19] Expected 0 arguments, but got 1.
[6, 19] Expected 0 arguments, but got 1.
[8, 19] Expected 0 arguments, but got 1.
[10, 19] Expected 0 arguments, but got 1.

For Excel Tables, getRange() works a bit differently than it does for the worksheet. Unlike the worksheet, getRange() for the Table accepts zero arguments. It's a function call that returns the range associated with the table. You can get the range associated with a column if you use the getColumn() function. From what I can tell, none of the column functions for Excel Tables support column letters or a range of column letters.
The easiest fix is to use the worksheet that contains the table. Once you have that, you can just update your code that hides the columns to use the worksheet instead of the lockbox. After you do that, the code should work as expected. You can see an example of how you might do that here:
function main(workbook: ExcelScript.Workbook) {
let lockbox = workbook.getTable("Lockbox");
let ws: ExcelScript.Worksheet = lockbox.getWorksheet()
// Set visibility of column(s) at range D:P on lockbox to true
ws.getRange("D:P").setColumnHidden(true);
// Set visibility of column(s) at range R:V on lockbox to true
ws.getRange("R:V").setColumnHidden(true);
// Set visibility of column(s) at range AB:AB on lockbox to true
ws.getRange("AB:AB").setColumnHidden(true);
// Set visibility of column(s) at range AE:AO on lockbox to true
ws.getRange("AE:AO").setColumnHidden(true);
}
You can also consolidate the lines to hide the columns into one line like so:
function main(workbook: ExcelScript.Workbook) {
let lockbox = workbook.getTable("Lockbox");
let ws: ExcelScript.Worksheet = lockbox.getWorksheet()
ws.getRanges("A:A,R:V,AB:AB,AE:AO").getAreas().forEach(cols=>cols.setColumnHidden(true));
}

Related

Officescript how to set value in table cell (row/column) by row index

pretty simple question but I can't seem to find what I am looking for and wondering if it is possible this way, just starting using Officescript/typescript. In a part of my code, I get the index of the row with a value that matches (cRow is the index of row I am interested in).
rowValue = collectionTable.getColumnByName("SomeCol").getRangeBetweenHeaderAndTotal().getValues()[cRow]
And then I run some checks on that row and want to update some other things based on the inputs.
So what I am expecting to do is something like the following, changing getValues for setValues:
collectionTable.getColumnByName("UpdateMe").getRangeBetweenHeaderAndTotal().setValues()[cRow]
OR
let col = collectionTable.getColumnByName("SomeCol").getIndex();
let cell = collectionTable.getCell(requestRow,col);
cell.setValue(value);
But doesn't seem to work that way .. From what I can tell, setValues works on ranges but can't quite find how to get the range/row by index number and set a value in one cell. I see all examples doing it with the letter and number but don't want to do it that way if possible.
Thanks for the help!
You can use getCell with the cRow variable after getRangeBetweenHeaderAndTotal() to get a specific cell range. Once you have that range, you can read its value using getValue(). And you can write a value to that range using setValue(). You can see an example of how to do that below:
function main(workbook: ExcelScript.Workbook)
{
let cRow: number = 1;
let tbl: ExcelScript.Table = workbook.getTable("table1");
let rowCell: ExcelScript.Range = tbl.getColumnByName("SomeCol").getRangeBetweenHeaderAndTotal().getCell(cRow,0);
let rowValue: string = rowCell.getValue() as string;
rowCell.setValue("some updated value");
}
Can get the value by using the worksheet cell, but I would really like to reference the table itself to not have any issues if things are moved around and such..
let col = collectionTable.getColumnByName("SomeCol").getIndex();
// if table starts at beginning of sheet, or else have to offset col and row..
let cell = dataSheet.getCell(requestRow + 1, col);
cell.setValue(value);

excel 2016 vba non-volatile user defined function recalculating unexpected

I am working with two structured table on two worksheets in one workbook.
I am using my UDF in SheetA which takes 3 arguments as string.
The first argument is ID column, the second argument is the first 8 characters on its column header cell, the third argument is the last 8 characters on its column header cell.
The UDF is applied to many other similar cells. The columns will be expanded as time pass by. ID and header cells are all static value. They are not derived from other cells.
The UDF will search TableB by "Application.Match" and draw values from matching rows and columns. If everything is valid, it will return appropriate results.
SheetA: TableA
ID
04/07/21 - 10/07/21
11/07/21 - 17/07/21
123456
=UDF(TableA:[#[ID]:[ID]],LEFT(TableA[[#Headers],[04/07/21 - 10/07/21]],8),Right(TableA[[#Headers],[04/07/21 - 10/07/21]],8))
=UDF(TableA:[#[ID]:[ID]],LEFT(TableA[[#Headers],[11/07/21 - 17/07/21,8),Right(TableA[[#Headers],[11/07/21 - 17/07/21]],8))
AABBCC
=UDF(TableA:[#[ID]:[ID]],LEFT(TableA[[#Headers],[04/07/21 - 10/07/21]],8),Right(TableA[[#Headers],[04/07/21 - 10/07/21]],8))
=UDF(TableA:[#[ID]:[ID]],LEFT(TableA[[#Headers],[11/07/21 - 17/07/21,8),Right(TableA[[#Headers],[11/07/21 - 17/07/21]],8))
SheetB: TableB
Date
123456
AABBCC
04/07/21
5.0
--
05/07/21
--
7.5
06/07/21
--
7.5
07/07/21
8
--
08/07/21
--
--
09/07/21
--
--
10/07/21
--
--
11/07/21
--
--
My problem is that my UDF is causing time consuming calculation whenever I edit the tables (i.e. volatile) even I'm not editing those three arguments/related cells. Also, UDF recalculate whenever I collapse/expand grouped table columns. I would like to make the UDF recalculate only (when one of the arguments changes) or (if the values in "SumRange" changes). The result should behave somewhat like an index/match formula, when the matching range is updated.
Here shows the UDF:
Function UDF(ID As String, Date1 As String, Date2 As String) As Variant
TargetTable = "TableB"
Set WS1 = ThisWorkbook.Worksheets("SheetB")
With WS1
matchCol = Application.Match(ID, .ListObjects(TargetTable).HeaderRowRange, 0)
If IsError(matchCol) Then
UDF = "ID not found"
Exit Function
End If
matchRow1 = Application.Match(CLng(CDate(Date1)), .ListObjects(TargetTable).ListColumns("Date").Range, 0)
matchRow2 = Application.Match(CLng(CDate(Date2)), .ListObjects(TargetTable).ListColumns("Date").Range, 0)
Set SumRange = .Range(.Cells(matchRow1, matchCol), .Cells(matchRow2, matchCol))
Arr = SumRange.Value
For Each cel In Arr
If Len(Trim(cel)) > 0 Then
UDF = Application.Sum(SumRange)
Exit Function
End If
Next cel
End With
UDF = ""
End Function
I cannot duplicate your problem.
If I change a value in TableB the UDF does NOT recalculate (because it is not volatile and nothing in the UDF argument list has changed).
If I change something in Table A the UDF recalculates because all its fields are either formulas or referenced by the UDF.

Error 2042 in Vlookup VBA despite type and value equality

Summerization: Despite seeming equality of two strings, they do not match match the text in their respective cells. Even though the third one does match with its.
I was given task "if possible" to simplify navigating through a complex excel WorkBook. I figured out a better way to generate IDs, as the old ones are almost nonsensical.
My IDs are created from 3 different numbers and the macro I'm making reverses these IDs with description explaining what the ID stands for.
Example: 550000210 first five stand for the account, the two after for the purpose and last two for the distribution (method). All 3 IDs are of the same format (or so I think so) and they are all strings(text). The final ID is a string(text), too.
To me surprise, however, it is not easy to use VLookup function in VBA and besides the first 5 digits, the two pairs don't match with the values in the ranges they refer too.
That made me think the named ranges within the "legenda" sheet do not share format, as the account number is generated by an online system but the pairs are by me. So I removed the formating and copied the format of the account numbers and it didn't change anything, tried double instead didn't help.
Using add watch, it says number I expect it to find is the same type "Variant/String", when I make an if condition where i compare them, it goes through successfully.
I'm really getting desperate, I do not necessarily have a time pressure but this was supposed to be a minor step to a "try" attempt to see if it goes anywhere for the task.
Let sel = Selection.Value 'user selects the code they want (vbModeless)
Let a = Left(sel, 5) 'first 5 digits
Let b = Mid(sel, 6, 2) 'the two after
Let c = Right(sel, 2) 'left two digits
Label1.Caption = a
Label2.Caption = b
Label3.Caption = c
Dim sourceSheet As Worksheet
Dim targetWorkbook As Workbook
Set targetWorkbook = Application.ActiveWorkbook
Set legendaSheet = targetWorkbook.Worksheets("legenda")
Dim ra As Range, rb As Range, rc As Range
Set ra = legendaSheet.Range("account")
Set rb = legendaSheet.Range("purpose")
Set rc = legendaSheet.Range("distribution")
'changes single digit back to single digit (needed)
'code does not work even when this condition isn't needed (aka numbers 10 or higher)
If Left(b, 1) = "0" Then
b = Replace(b, "0", "")
End If
If Left(c, 1) = "0" Then
c = Replace(c, "0", "")
End If
Let e = Application.VLookup(a, ra, 2, False) 'works here
Let f = Application.VLookup(b, rb, 1, False) 'error 2042
Let g = Application.VLookup(c, rc, 1, False) 'error 2042
Label4.Caption = e
Label5.Caption = f
Label6.Caption = g
Expected result is having the part of ID in left column of labels and description in the right one. It works only for "a". I suspect the issue really still is formating however.
Error 2042 is returned when VLOOKUP does not find the value. It would be #N/A if VLOOKUP was used in a formula (entered into a cell).
First of all, make sure that the data types match. Your variables a, b, and c are of type String, which stores text data. Most probably rb and rc contain numbers instead of text. So you have to convert b (and c) to a numeric value:
Let f = Application.VLookup(CLng(b), rb, 1, False) 'error 2042
Second, you should prepare your code for the case when no value is found:
Let f = "not found"
On Error Resume Next
f = Application.VLookup(CLng(b), rb, 1, False) 'error 2042
On Error Goto 0
' If found, f is the value from the sheet, otherwise "not found"
Alternatively, you may convert the data on the sheet to the correct type.
Update
OK, so the problem was that you want to search in the second column of purpose. To do this, you have to adjust that rb:
Set rb = legendaSheet.Range("purpose").Offset(0, 1)
If the column you want to return from the row found is before the one you search in, then you will have to use the INDEX and MATCH functions.

VBA - sum if rows hidden below

I am making a org chart with values attached at each level. I already have a simple hide rows VBA. How can I add on to it so that for example
if the value of Company A (e.g. J11) = values of Companies 1, 2, and 3, which are displayed in H14, J14, and L14
then when row 14 is hidden, then J11= sum(H14, J14, L14)
and when row 14 is visible, then J11 = 0
This is what i have so far just to hide/unhide rows.
Sub sbHideAll()
Rows("10:25").EntireRow.Hidden = True
End Sub
Sub sbShowAll()
Call sbHideAll
Rows("10:25").EntireRow.Hidden = False
End Sub
Sub sbShowGUCL()
Call sbHideAll
Rows("10:11").EntireRow.Hidden = False
End Sub
You can't do it directly but you can get the value of the visible cells.
So take the value of the range like this:
Application.WorksheetFunction.Sum(Union(Range("H13:H100"), Range("J13:J100"), Range("L13:L100")))
then take the value of the visible cells using .Rows.SpecialCells(xlCellTypeVisible) like this:
Application.WorksheetFunction.Sum(Union(Range("H13:H100").Rows.SpecialCells(xlCellTypeVisible), Range("J13:J100").Rows.SpecialCells(xlCellTypeVisible), Range("L13:L100").Rows.SpecialCells(xlCellTypeVisible)))
Then minus one from the other
You can either assign each to a variable or just plop it straight into J11 like so:
Range("J11").Formula = Application.WorksheetFunction.Sum(Union(Range("H13:H100"), Range("J13:J100"), Range("L13:L100"))) - Application.WorksheetFunction.Sum(Union(Range("H13:H100").Rows.SpecialCells(xlCellTypeVisible), Range("J13:J100").Rows.SpecialCells(xlCellTypeVisible), Range("L13:L100").Rows.SpecialCells(xlCellTypeVisible)))

Excel VBA: Conditional Format of Pivot Table based on Column Label

(Note: This is my first question. Please let me know if I can improve how I ask or explain)
I have created a module that can look through a pivot table on any excel worksheet and apply conditional formatting to each column to create quartile performances.
The pivot table can be any size and have different columns at any point - so I can't explicitly reference a particular column name or caption etc
Additionally, the quartile reporting identifies the top 25% for a particular figure, the next 25, next 25 and bottom 25.
For some figures high is better; for others, low is better.
So I list all the values that are high-to-low in an array, then run a quick "if name in array, rank this way; otherwise, rank this way" function.
All of this works like a dream - until we come to certain fields that result in an ambiguous name issue (and error). It seems that some caption names are similar to the source or database names.
The code reads like this (below).
Any ideas how I can refer dynamically to the column name and identify the column beneath it, please?
Dim myColumnNames As Variant
myColumnNames = VBA.Array("CONTRACTGROSSVOLUME", _
"MigrationVolume") 'etc
' Set colour choices for quartiles
Dim myQuartile1 As Long
Dim myQuartile2 As Long
Dim myQuartile3 As Long
Dim myQuartile4 As Long
myQuartile1 = RGB(146, 208, 80) 'Top Quartile - Green
myQuartile2 = RGB(255, 255, 0) '2nd Quartile - yellow
myQuartile3 = RGB(255, 192, 0) '3rd Quartile - orange
myQuartile4 = RGB(255, 0, 0) 'Bottom Quartile - red
Dim myRankingFactor As Boolean
myRankingFactor = True 'true is low to high / false is high to low ranking
Dim myPivotTable As PivotTable
Dim myPivotTableName As String
For Each myPivotTable In ActiveSheet.PivotTables
myPivotTableName = myPivotTable.Name
Next
Dim myPivotField As PivotField
Dim myPivotSourceName As String
Set myPivotTable = ActiveSheet.PivotTables(myPivotTableName)
For Each myPivotField In myPivotTable.DataFields
'get the source name for the pivot field
myPivotSourceName = myPivotField.Name
'Check if column name is in our list to rank high-to-low
If Not IsError(Application.Match(myPivotSourceName, myColumnNames, False)) Then
'This column name is in our list of names that should be ranked high-to-low (ie. Higher is better)
myRankingFactor = True
Else
'This column name is not in our list and should be ranked low-to-high (ie. lower is better)
myRankingFactor = False
End If
The error then comes on the following line:
myPivotTable.PivotSelect (myPivotSourceName), xlDataOnly, True
I've tried refering to the column with .caption, .name etc - no avail.
Any dieas on what I need to do to dynamically get the column name, check if it's in the array of names, then refer to that entire column to apply my formatting, please?
Thanks
Additional info:
The value is passing (apparently) correctly.
The column name displayed is "My Volume" and the variable is displaying "My Volume" as the value.
It's source name is "MYVOLUME" (one word), which I've also tried referencing without success.
The error generated is:
Run-time error '1004': An item name is ambiguous. Another field in the
pivottable report may have an item with the same name. Use the syntax
field [item]. For example, if the item is Oranges and the field is
Product, you would use Product[Oranges].
As an addition, I just manually changed one of the column names to a unique, single word ("ABCDEFG") that is not present in the database, object or anywhere in the data output to see if it would be picked up.
Changing that alias/caption value worked fine and didn't error.
Summary:
It's behaving as if the column name is already used elsewhere in the pivot - but it is not.
How do I explicitly refer to the column name/caption/label, but on-the-fly? :)
FIXED!
I ensured that the pivot table column name was passed as a string:
myPivotSourceName = myPivotField.Name
Then rather than referencing the data field with the pivot field object, I referenced the DataRange with the string:
myPivotTable.PivotFields(myPivotSourceName).DataRange.Select
Works perfectly and is completely portable for any pivottable on any sheet with any fields
I could reproduce the error by having a data item with the same name as the one of the data fields. For example, if you have the following table from which you create a pivot:
Product Price
Cola 123
Fanta 456
Sum of Price 789
then by creating a pivot table, you will have these items: Cola, Fanta, 'Sum of Price', and the following field labels: 'Row labels', 'Sum of Price'.
If you try to use 'Sum of Price' in the PivotTable.PivotSelect function, then the error message in the question will appear.
I think the Name parameter in PivotSelect is not clear enough, I did not find the documentation of the naming convention used in it, so I recommend referring to the datafield explicitly:
myPivotTable.PivotFields("Sum of Price").DataRange.Select
Note: There are many stylistic errors a superfluous parts in your code, e.g. the parentheses in the line that causes the error are not required, the loops just select the last item.
myPivotTable.PivotFields(myPivotSourceName).DataRange.Select
This references the name of the column as a string. Works on all pivot tables I've tested it on.

Resources