Want to use my google sheet custom search menu for multiple tab sheets - javascript

I have been trying to make a custom menu for searching data from my another tab Sheet but I want to make that for multiple sheets please can anyone help me so that I can fix this problem I am sharing my code here I tried by using sheet array and looping around but then multiple times that search pop-up coming.
function onOpen() {
let ui=SpreadsheetApp.getUi();
let menu= ui.createMenu('menu')
menu.addItem('Search','showInputBox');
menu.addItem('Clear','ClearCells');
menu.addToUi();
}
function ClearCells() {
var sheet = SpreadsheetApp.getActive().getActiveSheet();
sheet.getRange('A2:O500').clearContent();
}
function showInputBox() {
let ui = SpreadsheetApp.getUi()
let input = ui.prompt("Enter the searching item", ui.ButtonSet.OK_CANCEL);
if (input.getSelectedButton() == ui.Button.OK) {
let ss = SpreadsheetApp.getActiveSpreadsheet();
let ws = ss.getSheetByName('APAC');
let data = ws.getRange("A2:O47").getValues();
let userText = input.getResponseText();
let newData = data.filter(r => {
return r.some(cell => String(cell ? cell : "").trim() === userText);
});
let newWs = ss.getSheets()[0];
newWs.getRange(2,1, newData.length, newData[0].length).setValues(newData);
}
else if (input.getSelectedButton() == ui.Button.CANCEL) {
//Do something
}
else if (input.getSelectedButton() == ui.Button.CLOSE) {
//Do something
}
else {
}
}
I tried this code but need little help
var sheetListArray = ["Sheet1", "Sheet2"]
var ss = SpreadsheetApp.getActiveSpreadsheet();
for( var i = 1 ; i <= sheetListArray.length ; i++)
showInputBox(sheetListArray[i]); }

function showInputBox() {
let ui = SpreadsheetApp.getUi()
let input = ui.prompt("Enter the searching item", ui.ButtonSet.OK_CANCEL);
if (input.getSelectedButton() == ui.Button.OK) {
let ss = SpreadsheetApp.getActiveSpreadsheet();
let ws = ss.getSheetByName('APAC');
let data = ws.getRange("A2:O47").getValues();
let tf = ws.createTextFinder(input.getResponseText()).findAll();
let html = '';
tf.forEach(f => {
html += `row: ${f.getRow()} col: ${f.getColumn()}`
});
ui.showModelessDialog(HtmlService.createHtmlOutput(html),'Results')
} else {
us.showModelessDialog(HtmlService.createHtmlOutput('No results found'));
}
}

Related

Method search on google sheet data from one column using google script?

I had tried to search data like below flow picture and script to search data from google sheet using google app script but the script using is not working properly but can someone tell me how to setup search function to find data like flow on image? thanx
[Flow searching data][1]
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [ {name: "Search", functionName: "searchRecord"} ];
ss.addMenu("Commands", menuEntries);
}
function searchRecord()
{
var ss = SpreadsheetApp.getActiveSpreadsheet()
var wsSearchingData = ss.getSheetByName("Searching Data")
var wsDatabase = ss.getSheetByName("Database")
var searchString = wsSearchingData.getRange("E4").getValue();
var column =1; //column Index
var columnValues = wsDatabase.getRange(2, column, wsDatabase.getLastRow()).getValues(); //1st is header row
var searchResult = columnValues.findIndex(searchString); //Row Index - 2
var searchValue = wsDatabase.getRange("B2:B2041").getValues()
var matchingDatabase = searchValue.map(searchColumn => {
var matchColumn = columnValues.find(r => r[0] == searchColumn[0])
return matchColumn = matchColumn ? [matchColumn[2]] : null
})
console.log(matchingDatabase)
if(searchResult != -1)
{
//searchResult + 2 is row index.
SpreadsheetApp.getActiveSpreadsheet().setActiveRange(sheet.getRange(searchResult + 1, 1))
}
Array.prototype.findIndex = function(search){
if(search == "") return false;
for (var i=0; i<this.length; i++)
if (this[i] == search) return i;
wsSearchingData.getRange("B11").setValue(search[0]);
wsSearchingData.getRange("C11").setValue(search[1]);
wsSearchingData.getRange("D11").setValue(search[2]);
wsSearchingData.getRange("E11").setValue(search[3]);
wsSearchingData.getRange("F11").setValue(search[4]);
return;
}
}
[1]: https://i.stack.imgur.com/HF9K8.png
var searchResult = columnValues.findIndex(searchString); //Row Index - 2
replace the above code with:
var searchResult = columnValues.filter(r=>r[1]==searchString)
You can then put searchResult directly as output in the sheet. Make sure that [1] in the above contains the column index of Name in the columnValues Array.

Script to Transfer Column Values to 1 of 4 Columns Based on Empty Columns

I'm trying to figure out a script to automate the transfer of data from a column on one sheet, to one of four columns on another sheet. The data being transferred is an order that has been placed, so the data needs to stay in the column its transferred to until it is received. So basically the script needs to find the next blank column and transfer the data there.
For reference, the sheet is HERE:https://docs.google.com/spreadsheets/d/1DxafMMZL7Nc2M4A41Dzssf9dgrzYZgsX1qKgtfhLMlM/edit?usp=sharing. The data that will be transferred is Ordering!P5:P, and will be transferred from there to Pending TOs, either E6:E, F6:F, G6:G or H6:H, based on whether the previous column already has values.
I wrote the following script, but it only worked on the initial test, and fails to detect empty columns F:H. After clearing column E, I could not repeat the initial success.
Any guidance is greatly appreciated.
function PasteTOTEST() {
var ss = SpreadsheetApp.getActive();
const ui = SpreadsheetApp.getUi();
const response = ui.alert('Table is full', ui.ButtonSet.OK);
var copyFrom = ss.getRange('Ordering!P5:P');
var copyTo = ss.setActiveSheet(ss.getSheetByName('Pending TOs'));
var toColumnA = copyTo.getRange('E6:E').activate();
var toColumnB = copyTo.getRange('F6:F').activate();
var toColumnC = copyTo.getRange('G6:G').activate();
var toColumnD = copyTo.getRange('H6:H').activate();
if(ISBLANK(toColumnA)) {
toColumnA;
copyFrom.copyTo(ss.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
}
else if(ISBLANK(toColumnB)) {
toColumnB;
copyFrom.copyTo(ss.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
}
else if(ISBLANK(toColumnC)) {
toColumnC;
copyFrom.copyTo(ss.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
}
else if(ISBLANK(toColumnD)) {
toColumnD;
copyFrom.copyTo(ss.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
}
}
This function will paste the data in the first available column in ranges E6:E, F6:F, G6:G or H6:H
function PasteTOTEST() {
var ss = SpreadsheetApp.getActive();
const ui = SpreadsheetApp.getUi();
const fsh = ss.getSheetByName('Ordering');
const tsh = ss.getSheetByName('Pending TOs');
let frg = fsh.getRange(5, 16, getColHite(16, fsh, ss) - 4, 1);
let ar = availableRanges();
if(ar && ar.length) {
trg = tsh.getRange(6, ar[0]);
frg.copyTo(trg, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
} else {
ui.alert('Table is full', ui.ButtonSet.OK);
}
}
function getColHite(col, sh, ss) {
var ss = ss || SpreadsheetApp.getActive();
var sh = sh || ss.getActiveSheet();
var col = col || sh.getActiveCell().getColumn();
var rcA = [];
if (sh.getLastRow()) { rcA = sh.getRange(1, col, sh.getLastRow(), 1).getValues().flat().reverse(); }
let s = 0;
for (let i = 0; i < rcA.length; i++) {
if (rcA[i].toString().length == 0) {
s++;
} else {
break;
}
}
return rcA.length - s;
}
function clearAllRanges() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Pending TOs');
[...Array.from(Array(4).keys(), x => x + 5)].forEach(n => {
let h = getColHite(n, sh, ss)
if(h>4)sh.getRange(5, n, h - 4, 1).clearContent()
});
}
function availableRanges() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Pending TOs');
let r = [...Array.from(Array(4).keys(), x => x + 5)].reduce((a,v,arr) => {
let h = getColHite(v, sh, ss);
if(h < 6) a.r.push(v);
return a;
},{r:[],availableRanges:function(){return this.r;}}).availableRanges();
//Logger.log(r);
return r;
}
Demo:
In case you are wondering what this does:
[...Array.from(Array(4).keys(), x => x + 5)]
It just feeds the four column numbers into the reduce loop current value

Placing a timestamp on a cell accross multiple sheets on edit

I tried many different variations of this script and can't figure out why it's not working.
I want to update the same cell "J3" with a timestamp across multiple tabs onedit. I have another function on the spreadsheet already using onEdit(e), so i set a new function with an onEdit trigger.
I retrieve the names of the tabs I want to update and store them in an array.
function Overview_timestamp(e) //function to use in the onEdit trigger
{
var activeSheet = e.source.getActiveSheet();
var range = e.range;
var cellToStamp = "J3" ;
var name_range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Structure').getRange(2, 5, 14, 1).getValues(); //sheets i want to update
var flattened_name_range = [] ; //converting into 1D array
for (var i = 0; i < name_range.length; i++) {
flattened_name_range.push(name_range[i][0]);
}
if(flattened_name_range.indexOf(activeSheet())!==-1 ) {
activeSheet.getRange(cellToStamp).setValue(new Date()).setNumberFormat("yyyy-MM-dd hh:mm:ss");}
}
Try this:
function Overview_timestamp(e) {
const sh = e.range.getSheet();
if (sh.getName() == 'Sheet name') {//just to limit action to one sheet but it would be nice to have additional control like clicking a checkbox or selecting a value on a validated cell.
const shts = e.source.getSheetByName('Structure').getRange(2, 5, 14).getValues().flat();
shts.forEach(n => {
e.source.getSheetByName(n).getRange('J3').setValue(new Date()).setNumberFormat("yyyy-MM-dd hh:mm:ss")
});
}
}
I did it this way on my sheet using a checkbox at A1 on Sheet1
function onEdit(e) {
//e.source.toast('entry');
const sh = e.range.getSheet();
if (sh.getName() == 'Sheet1' && e.range.columnStart==1 && e.range.rowStart==1 && e.value=='TRUE') {
//e.source.toast('cond');
const shts = e.source.getSheetByName('Sheet2').getRange(2, 5, 14).getValues().flat().filter(e=>e);
shts.forEach(n => {
e.source.getSheetByName(n).getRange('J3').setValue(new Date()).setNumberFormat("yyyy-MM-dd hh:mm:ss")
});
e.range.setValue('FALSE');
}
}

refresh drop down list after button click in web app

I have a web app with one drop down list and 2 buttons. The drop down list get values from a sheet. The buttons write back in the sheet. The script I have works fine with that:
<script>
$(function() {
$('#txt1').val('');
google.script.run
.withSuccessHandler(updateSelect)
.getSelectOptions();
});
function updateSelect(opt)
{
var select = document.getElementById("sel1");
select.options.length = 0;
for(var i=0;i<opt.length;i++)
{
select.options[i] = new Option(opt[i],opt[i]);
}
}
function listS() {
const selectElem = document.getElementById('sel1')
const index = selectElem.selectedIndex;
if (index > -1) {
const e = document.getElementById("sel1");
const value = e.options[index].value;
const body = { index: index, value: value };
google.script.run.withSuccessHandler(yourCallBack).yourServerSideFunc(body);
}
}
document.getElementById("but1").addEventListener("click",listS);
function yourCallBack(response) {
}
</script>
In Java script:
function getSelectOptions()
{
var ss=SpreadsheetApp.openById('1onuWoUKh1XmvEAmKktwJekD782BFIru-MDA0omqzHjw');
var sh=ss.getSheetByName('Database');
var rg=sh.getRange(2,1,sh.getLastRow()-1,8);
var vA=rg.getValues();
var useremail = Session.getActiveUser().getEmail();
var opt=[];
for(var i=0;i<vA.length;i++)
{
if(vA[i][1] == "Pending Approval"){
if(vA[i][7]+"#xxx.com" == useremail || vA[i][7]+"#xxx.com" == useremail) {
opt.push(vA[i][3]+" REQ ID: "+vA[i][0]);
}
}
};
if (opt.length == 0) {opt.push("You do not have pending requests")};
return opt;
}
function doGet() {
var output = HtmlService.createHtmlOutputFromFile('list');
return output;
}
function yourServerSideFunc(body) {
var value = body["value"];
var ss = SpreadsheetApp.openById('1onuWoUKh1XmvEAmKktwJekD782BFIru-MDA0omqzHjw');
var sh = ss.getSheetByName('Database');
var rg=sh.getRange(1,1,sh.getLastRow()-1,4);
var vA=rg.getValues();
var str = "Approved";
for(var i=0;i<vA.length;i++)
{
if(vA[i][3]+" REQ ID: "+vA[i][0] == value) {
sh.getRange(i+1, 2).setValue(str);
}
};
return ContentService.createTextOutput(JSON.stringify({message: "ok"})).setMimeType(ContentService.MimeType.JSON);
Now I am trying to regenerate the drop down list values after the button is clicked. I tried to add
var output = HtmlService.createHtmlOutputFromFile('list');
return output;
in yourServerSideFunc(body) function to regenerate the HTML but does not work. I have tried to force a HTML refresh, but also did not work.
How can I easily re-trigger the generation of the drop down list items? Worst case scenario it is ok to refresh the whole page, but it should be simple to regenerate the drop down list since I have already the code for it.
I ended up with this work around.
function listS() {
const selectElem = document.getElementById('sel1')
const index = selectElem.selectedIndex;
if (index > -1) {
const e = document.getElementById("sel1");
const value = e.options[index].value;
const body = { index: index, value: value };
google.script.run.withSuccessHandler(yourCallBack).yourServerSideFunc(body);
//ADDED:
var select = document.getElementById("sel1");
select.options[index] = new Option("Approved! Please refresh","Approved! Please refresh");
selectElem.selectedIndex = index;
}
}
It does not really meet the original goal to refresh the list from the sheet. It would be great if someone else posted a solution to call the server function. I tried to add google.script.run.doGet() and similar, but it seems that it does not call the server side functions properly.

Sheet Coming in as undefined

I have a spreadsheet with revenue and expense data. Here is a spreadsheet with what it looks like with data redacted: https://docs.google.com/spreadsheets/d/1xYXt_pvhpzCO7D61SoOwcmNNIGaFHkExwLHhl56c-NU/edit?usp=sharing
I want to create a script that validates the data in column C by forcing a user to enter a date. Once a date is entered, I want to return a new row beneath it. The code works in that it validates data and enters a new row, but when I enter a date into C96, the new row pushes the 'Date' cell, currently in C100, down to C101 and says the date is invalid. I set up my code so that it should be rerunning the script and not making C101 data validated. Additionally, I am getting errors with the 'getRange' method. It is saying that 'TypeError: Cannot call method "getRange" of undefined.'
Currently I have triggers for onEdit and onOpen for both dateValidation functions and onEdit triggers for the addRow functions.
For some reason, on my original file, the code still runs despite these errors. I have starred the location of the errors in my code below. I am very new to Google Scripts/ programming in general, so if anyone can help me out it would be greatly appreciated.
Here is my code currently:
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("General Ledger");
// Get row of first revenue
var firstRowRevLookup = 'Revenue';
var firstRowRange = sheet.getRange('B1:B');
var firstRowValues = firstRowRange.getValues();
var x = [];
for (var i = 0; i<firstRowValues.length; i++) {
if(firstRowValues[i] == firstRowRevLookup) {
x.push(i+3);
}
}
var firstRowRevenue = Number(x);
Logger.log(firstRowRevenue);
//Get row of last revenue
var lastRowRevLookup = 'End Revenues';
var lastRowRange = sheet.getRange('C1:C');
var lastRowValues = lastRowRange.getValues();
var y = [];
for (var i = 0; i < lastRowValues.length; i++) {
if(lastRowValues[i] == lastRowRevLookup) {
y.push(i);
}
}
var lastRowRevenue = Number(y);
Logger.log(lastRowRevenue)
//Get row of first expense
var firstRowExLookup = 'Expenses';
var z = [];
for(var i = 0; i<firstRowValues.length; i++) {
if(firstRowValues[i] == firstRowExLookup){
z.push(i+3);
}
}
var firstRowExpense = Number(z);
Logger.log(firstRowExpense);
//Get row of last expense
var lastRowExLookup = 'End Expenses';
var q = [];
for(var i = 0; i <lastRowValues.length; i++) {
if( lastRowValues[i] == lastRowExLookup) {
q.push(i);
}
}
var lastRowExpense = Number(q);
Logger.log(lastRowExpense);
dateValidationExpenses(sheet, firstRowExpense, lastRowExpense);
dateValidationRevenue(sheet, firstRowRevenue, lastRowRevenue);
//User must enter date into column C
function dateValidationExpenses(sheet, firstRowExpense, lastRowExpense) {
// Logger.log(sheet);
**var dateRange = sheet.getRange(firstRowExpense, 3, lastRowExpense-firstRowExpense+1);**
//Logger.log(dateRange);
var rule = SpreadsheetApp.newDataValidation()
.requireDate()
.setAllowInvalid(false)
.setHelpText('Please enter date using format "=date(year,month,day)"')
.build();
dateRange.setDataValidation(rule);
dateRange.setFontWeight('normal');
}
//User must enter date into column C
function dateValidationRevenue(sheet,firstRowRevenue,lastRowRevenue) {
**var dateRange = sheet.getRange(firstRowRevenue, 3, lastRowRevenue-firstRowRevenue+1);**
var rule = SpreadsheetApp.newDataValidation()
.requireDate()
.setAllowInvalid(false)
.setHelpText('Please enter a date using format "=date(year,month,day)"')
.build();
dateRange.setDataValidation(rule);
dateRange.setFontWeight('normal');
}
function addRowExpenses () {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('General Ledger');
var testCell = sheet.getRange(lastRowExpense,3).getValue();
//Logger.log(testCell);
if (testCell !== '') {
sheet.insertRows(lastRowExpense+1);
}
}
function addRowRevenue () {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('General Ledger');
var testCell = sheet.getRange(lastRowRevenue,3).getValue();
//Logger.log(testCell);
if (testCell !== '') {
sheet.insertRows(lastRowRevenue+1);
}
}
function editTrigger1() {
var sheet = SpreadsheetApp.getActive()
ScriptApp.newTrigger('addRowExpenses')
.forSpreadsheet(sheet)
.onEdit()
.create();
}
function editTrigger2() {
var sheet = SpreadsheetApp.getActive()
ScriptApp.newTrigger('addRowRevenue')
.forSpreadsheet(sheet)
.onEdit()
.create();
}
The variable sheet is declared outside of a function, which makes it a "global". You don't need to pass sheet
Currently:
dateValidationExpenses(sheet, firstRowExpense, lastRowExpense);
dateValidationRevenue(sheet, firstRowRevenue, lastRowRevenue);
Change to:
dateValidationExpenses(firstRowExpense, lastRowExpense);
dateValidationRevenue(firstRowRevenue, lastRowRevenue);
sheet is available to the dateValidationExpenses function without the need to pass it. That should solve the issue with the error you are getting on this line:
var dateRange = sheet.getRange(firstRowExpense, 3, lastRowExpense-firstRowExpense+1);

Categories