Sep 07 2023 05:51 AM - edited Sep 11 2023 12:50 PM
So I have suggested that excel add an 'ENTRY LINE' to tables. See that suggestion here:
https://feedbackportal.microsoft.com/feedback/idea/ae0a8991-71fa-ed11-a81c-000d3ae5b6f4
I image it would look something like this (EDIT: updated image):
So you can enter data at the top and hit enter and it would be added to the table without scrolling to the bottom.
Well, in the mean time, I made the script and that button is fully functional and because it is a script it will work Both online and in the app. See attached file.
Please let me know you LIKE it with a thumbs up or better yet leave a comment to let me know if you tried it out. Also welcoming feedback and improvements. I'm already considering to expand this to cycle through all tables on the sheet. Here is the present script (updated script):
/**
* This script inserts the row above a table into the table.
*/
function main(workbook: ExcelScript.Workbook) {
let wksht = workbook.getActiveWorksheet();
//Unlock worksheet
let wkshtProtect = wksht.getProtection().getProtected();
if (wkshtProtect) {wksht.getProtection().unprotect(); };
//find active cell and 3x3 around that cell as 'activeBubble' to find correct table
let activeCell = workbook.getActiveCell();
let aMinRow = activeCell.getRowIndex() - 1;
if (aMinRow < 0) { aMinRow = 0; };
let aMinCol = activeCell.getColumnIndex() - 1;
if (aMinCol < 0) { aMinCol = 0; };
let activeBubble = wksht.getRangeByIndexes(aMinRow, aMinCol, 3, 3).getAddress();
//Check each table if row above overlaps activeBubble
let tCount = wksht.getTables().length;
let foundTable = 0;
// let tbl = wksht.getTables().shift()
wksht.getTables().forEach( (tbl) => {
foundTable= Math.abs(foundTable);
let newData = tbl.getHeaderRowRange().getOffsetRange(-1, 0);
if (wksht.getRange(newData.getAddress()).getIntersection(activeBubble)) {
//let remFormat = newData.getFormat();
//tbl.addRows(-1, newData.getValues());
tbl.addRow();
let newRow = tbl.getRange().getLastRow(); //.getTotalRowRange().getLastRow();
let n = newData.getColumnCount();
for (let i = 0; i< n; i++) {
//selectively inserts data from input line (i.e. do not overwrite table formulas)
if ( !newRow.getCell(0, i).getFormula().toString().startsWith("=") ) {
newRow.getCell(0, i).copyFrom(newData.getCell(0, i), ExcelScript.RangeCopyType.values);
};
//selectively clear input line (i.e. do not clear formulas)
if ( !newData.getCell(0, i).getFormula().toString().startsWith("=") ) {
newData.getCell(0, i).setValue(""); //.clear
};
};
//put cursor back on 'start' of input line
newData.getCell(0,0).select();
} else {foundTable++};
});
//console.log("tCount=" + tCount + " foundTable=" + foundTable);
if (tCount == foundTable){
//console.log("no table found next to last active cell.");
//return;
throw 'no table found next to last active cell';
}
//TEST CODE SECTION - IGNORE
//if it was protected, re-protect
if (wkshtProtect) {wksht.getProtection().protect();};
}
Sep 07 2023 05:59 AM
Idea is interesting, but the script is unshared. Could you add it to the post or share by other way?
Sep 07 2023 06:00 AM
Okay, I found it on the feedback. Will play a bit and vote.
Sep 07 2023 06:02 AM - edited Sep 07 2023 06:03 AM
I thought you could/would find it in the file but good point I added it here too. I'm still learning what is shared in the workbook itself vs. lives online
Sep 07 2023 07:12 AM
it's shared through your OneDrive permissions, not with the file.
I upvoted and added few comments.
Sep 07 2023 08:08 AM
Sep 07 2023 08:46 AM
shift(). I don't like an idea to loop all tables in active worksheet. Perhaps it's better getTable("TableName") but all depends on scenario. If to loop when at least ignore empty row under the each table.
filtering. Didn't play with the script itself and don't know what the solution is. But believe it exists.
formula. Didn't test but I guess now we break auto-fill of the table formula. Perhaps we may check any row in the table, if formula exists in the column when push it, otherwise value from the upper row. Yes, that's cell by cell but it shall be not critical from the performance point of view, we work only with one row. Recursion allows not care about number of cells.
In general that's interesting task, thank you for initiating it. Will try to play with script as well when have bit more time. If any results will share.
Sep 07 2023 02:39 PM
I modified a bit for the single table not to add empty row and to skip cells if the table has formula in related column
/**
* This script inserts the row above a table into the table.
*/
function main(workbook: ExcelScript.Workbook) {
let wksht = workbook.getActiveWorksheet()
let tbl = wksht.getTable("Table1")
const newData = tbl
.getHeaderRowRange()
.getOffsetRange(-1, 0)
if ( !newData
.getValueTypes()
.every(v => v[0] == ExcelScript.RangeValueType.empty) ) {
tbl.addRow()
const newRow = tbl
.getRangeBetweenHeaderAndTotal()
.getLastRow()
const n = newRow.getColumnCount()
const content = newRow.getFormulas()
for (let i = 0; i < n; i++) {
if( !content[0][i].toString().startsWith("=") ) {
newRow
.getCell(0, i)
.copyFrom( newData.getCell(0, i) )
}
}
newData.clear(ExcelScript.ClearApplyTo.contents)
}
}
Sep 11 2023 11:05 AM - edited Sep 11 2023 12:11 PM
@SergeiBaklan I've updated my script to:
a) find and only apply to a table next to the active cell
b) not overwrite table formulas (and still copy value only if formula in the insert line)
c) still do not delete formula from insert line
d) throw error (i.e. comment in log) if active cell is not next to a table
e) after entering data and clearing input line, set cursor to 1 cell on that line
I also created 2 'companion' scripts: 1 to clear the input line (i.e. change your mind and don't want that data) and 1 to use the input line as a filter for the table (actually need to finish this script)
Here is the new look:
and the updated code:
/**
* This script inserts the row above a table into the table.
*/
function main(workbook: ExcelScript.Workbook) {
let wksht = workbook.getActiveWorksheet();
//Unlock worksheet
let wkshtProtect = wksht.getProtection().getProtected();
if (wkshtProtect) {wksht.getProtection().unprotect(); };
//find active cell and 3x3 around that cell as 'activeBubble' to find correct table
let activeCell = workbook.getActiveCell();
let aMinRow = activeCell.getRowIndex() - 1;
if (aMinRow < 0) { aMinRow = 0; };
let aMinCol = activeCell.getColumnIndex() - 1;
if (aMinCol < 0) { aMinCol = 0; };
let activeBubble = wksht.getRangeByIndexes(aMinRow, aMinCol, 3, 3).getAddress();
//Check each table if row above overlaps activeBubble
let tCount = wksht.getTables().length;
let foundTable = 0;
// let tbl = wksht.getTables().shift()
wksht.getTables().forEach( (tbl) => {
foundTable= Math.abs(foundTable);
let newData = tbl.getHeaderRowRange().getOffsetRange(-1, 0);
if (wksht.getRange(newData.getAddress()).getIntersection(activeBubble)) {
//let remFormat = newData.getFormat();
//tbl.addRows(-1, newData.getValues());
tbl.addRow();
let newRow = tbl.getRange().getLastRow(); //.getTotalRowRange().getLastRow();
let n = newData.getColumnCount();
for (let i = 0; i< n; i++) {
//selectively inserts data from input line (i.e. do not overwrite table formulas)
if ( !newRow.getCell(0, i).getFormula().toString().startsWith("=") ) {
newRow.getCell(0, i).copyFrom(newData.getCell(0, i), ExcelScript.RangeCopyType.values);
};
//selectively clear input line (i.e. do not clear formulas)
if ( !newData.getCell(0, i).getFormula().toString().startsWith("=") ) {
newData.getCell(0, i).setValue(""); //.clear
};
};
//put cursor back on 'start' of input line
newData.getCell(0,0).select();
} else {foundTable++};
});
//console.log("tCount=" + tCount + " foundTable=" + foundTable);
if (tCount == foundTable){
//console.log("no table found next to last active cell.");
//return;
throw 'no table found next to last active cell';
}
//TEST CODE SECTION - IGNORE
//if it was protected, re-protect
if (wkshtProtect) {wksht.getProtection().protect();};
}
Sep 13 2023 08:24 AM
Looks great. That's interesting idea with table near the active cell. As variant for bubble area it could be
const activeBubble = activeCell
.getResizedRange(2, 2)
.getOffsetRange(
(activeCell.getRowIndex() == 0) ? 0 : -1,
(activeCell.getColumnIndex() == 0) ? 0 : -1
)
.getAddress()