I have a vlookup formula looking for names and returning the average for that person. I record these averages throughout the week, and identify them as W1, W2, W3, etc. in different columns. The problem is, the data I import only has ONE average on it, meaning that if I import it, it will override what I had already put in a few days ago using the same report, just an older version.
My question is, is there a way for me to stop the formula for W1 from updating when the data it is calling on changes? Essentially, to freeze the values? That way I don't have to keep adding new tabs to import the new data in an effort to save the history. The data is robust, and I am gathering more info than just averages, so I need the whole thing.
I wouldn't mind scripting something too that would help solve my issue, I just need some guidance.
Edit: thoughts on scripting a menu button that would copy, paste values only of a selected range that I could trigger right before I update the data?
I want to be able to select a range in my sheet, select the menu "Save Info", have it copy what I selected, and paste it right back where it already was. This will remove the formulas that would otherwise cause my values to change upon the new data import, and leave my history intact.
The following is what I have come up with so far, but I receive this error:
"TypeError: sourceSheet.getDataRange is not a function".
function saveInfo(){
var spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = spreadSheet.getActiveSheet
var sourceRange = sourceSheet.getDataRange();
var targetSheet = spreadSheet.getActiveSheet();
sourceRange.copyTo(targetSheet.getActiveRange());
}
function updateMenu() {
SpreadsheetApp.getActiveSpreadsheet().updateMenu('Save Info', generateMenu())
};
function onOpen() {
SpreadsheetApp.getActiveSpreadsheet().addMenu('Save Info', generateMenu());
};
function generateMenu() {
var entries = [{
name: "Save Data Before Update",
functionName: "saveInfo"
}];
return entries;
}
Any ideas??
Thanks!
Here you go:
function saveInfo() {
var range = SpreadsheetApp.getActiveSheet().getActiveRange();
var values = range.getDisplayValues();
range.setValues(values);
}
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Save Info')
.addItem('Save Data Before Update', 'saveInfo')
.addToUi();
}
The script converts data inside selected cells into a plain text.
But actually you don't need any script. You can just select the cells, press Ctrl+C and Ctrl+Shift+V.
Related
Is it possible to have triggers that work for 2 spreadsheets (2, independent files) with one script? I am stuck at how to do this part, as it needs to be automated. So far in my research I have seen importrange triggers but nothing that can do a prompt on a specific worksheet.
My goal is to make a "list veto" function with the following requirements:
Must use google spreadsheets
a) One Sheet with "triggers" that is the "controller" or moderator
b) One Sheet for the "preferred" elector
c) One Sheet for the "secondary" elector
The list will be dynamic, but for this example let us say it consists of a list of 15 locations.
Both "electors" will have to veto continuously until there is only 8 locations left.
The "controller" will trigger a system that asks the "preferred elector" first, then the "secondary," by a prompt that goes to their respective spreadsheet. Once the preferred makes a choice, the list of locations is deducted, then a prompt goes to the worksheet of the "secondary."
Triggers are set by spreadsheet, not by sheet, but you could use JavaScript control statements like if..else and switch to control when should be executed parts of your script.
Example:
function respondToOnEdit(e){
var sheetName = e.source.getActiveSheet().getName();
if(sheetName === 'Sheet1'){
// do something when Sheet1 is edited
} else if(sheetName === 'Sheet2'){
// do something when Sheet2 is edited
} else {
// do something when any other sheet is edited
}
}
You will have to use code to create an installable trigger for each spreadsheet.
Code snippet:
[id1, id2].forEach(id => {
var spreadsheet = SpreadsheetApp.openById(id);
ScriptApp.newTrigger('respondToOnEdit')
.forSpreadsheet(spreadsheet)
.create()
}
Related
Is there any way to automatically create installable triggers?
So I have two separate sheets, one with a data list full of unique names, their location and their current status. The second would have an input to type someones name and it would pull up the row that contains their current location/status(first function), from there I could change the status and run the second function that updates the first sheet with the new status. I have been googling all day trying to cobble together something that would work and I think I am almost there on the first function but I am stuck and any help to point me in the right direction would be greatly appreciated.
Ideally the first function would trigger upon typing the name in and the second would trigger upon changing the status but I can find a manual workaround if need be.
function dataPull() {
var data = SpreadsheetApp.openById('Spreadsheet_ID'); //replace with Data spreadsheet ID
var filteredRows = data.filter(function (data) {
var employee = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange('b2').getValue();
if (data[1] === employee) {
return data
}
});
SpreadsheetApp.getActiveSheet().getRange('A2:D').setValue(filter)
}
Here are some screenshots that will hopefully better explain what I am looking to accomplish.
Sample data
Type someones name in Input sheet
Pull up current status w/ function one
Update Status and run function two
Sample data updates to reflect change in status
Edit Dialog in a Sheet
There is two sections to this function. The top section triggers off of edits to the Edit sheet in column 2 and row 2 which is a dropdown that contains all of the name in the Data Sheet. An edit here cause the top section to run which reads the the data on the Data sheet and file a matching name when it finds it that line is loaded into columns 1 through 4 and row 2 of the Edit sheet. Then you can edit the status and click the checkbox to save it back to the Data sheet. The lower portion then runs which reset's the checkbox, clears the Edit range and loads the edits back into the Data Sheet.
Please take a look at the animation to see the process in action.
Just to let you know upfront, you can run this function from the script editor you must put it into you sheet and name your sheets properly. You must also set up the data validations for the two drop downs. If you try to run this function from the script editor you will get the Error: Cannot find method getsheet() of undefined
function onEdit(e) {
e.source.toast('Entry');
var sh=e.range.getSheet();
if(sh.getName()=='Edit' & e.range.columnStart==2 && e.range.rowStart==2) {
e.source.toast('flag1');
var found=false;
var dsh=e.source.getSheetByName('Data');
var vA=dsh.getRange(2,1,dsh.getLastRow()-1,dsh.getLastColumn()).getValues();
for(var i=0;i<vA.length;i++) {
if(vA[i][1]==e.range.getValue()) {
sh.getRange(2,1,1,4).setValues([vA[i]]);
found=true;
e.source.toast('Value Found at i = ' + i + 'value = ' + vA[i][1],'',-1);
break;
}
}
if(!found) {
e.source.toast('No Value found = ' + e.value);
}
Logger.log(vA);
}
if(sh.getName()=='Edit' & e.range.columnStart==5 && e.range.rowStart==2) {
e.range.setValue("FALSE");
dsh=e.source.getSheetByName('Data');
dsh.getRange(Number(e.range.offset(0,-4).getValue()+1),4).setValue(e.range.offset(0,-1).getValue());
sh.getRange(2,1,1,4).clearContent();
}
}
Data Sheet:
Edit Sheet:
Animation:
You can't use the built-in onEdit(e) trigger; as it behaves in the same manner as a custom function would eg.(Restricted to the sheet it's bound to).
You can install a trigger in your bound script, and have it execute your un-restricted function.
function importData(e){
// Get the cell where the user picked its name
var range = e.range;
// The Id of your data source sheet
var dataSheetId = 'XXXXXXXXXXXXX';
// Get all values from source sheet as an array
var data_values = SpreadsheetApp.openById(dataSheetId).getDataRange().getDisplayValues();
// Return the row with data for matching employee
var filteredRow = data_values.filter(check)[0];
// Input location data in input sheet
SpreadsheetApp.getActiveSheet().getRange(range.getRow(),range.getColumn()+1).setValue(filteredRow[2]);
}
function check(row) {
// Get the name that the user picked
var employee_name = SpreadsheetApp.getActiveSheet().getActiveCell().getValue();
// Check if it matches and return the row
if (row[1].toLowerCase() === employee_name.toLowerCase()) {
return row
}
}
You can install the trigger from within your Apps Script by following Edit/Current project's triggers/Add trigger.
As the event source for the trigger select: "From spreadsheet"; and for function to run select "importData" (or the name of your function).
On the advice of someone below, I am editing this post:
My initial goal is that when my Google sheet is opened, and every time it is opened, I would like the values of several Data Validation dropdown menus, currently located in Cells A10, A15, and A20, to be set to the option of "Select" -- which is a word in the validation range, along with 2 other text values.
I have been informed that this needs to be declared at a global scope -- but I am a complete script novice and, frankly, have no idea as to how to make this work.
Any advice will be greatly appreciated.
function onOpen() {
SpreadsheetApp.getActiveSheet().getRange('A10').setValue('Select');
SpreadsheetApp.getActiveSheet().getRange('A15').setValue('Select');
SpreadsheetApp.getActiveSheet().getRange('A20').setValue('Select');
Note for new readers:
The original code on the question was this
//When the sheet is opened, the contents of Cell A2 are cleared and the values in the Data Validation dropdown menus in Cells A10, A15, and A20 are set to the default "Select"
function myFunction() {
function onOpen() {
SpreadsheetApp.getActiveSheet().getRange('A2').clearContent();
SpreadsheetApp.getActiveSheet().getRange('A10').setValue('Select');
SpreadsheetApp.getActiveSheet().getRange('A15').setValue('Select');
SpreadsheetApp.getActiveSheet().getRange('A20').setValue('Select');
}
//When the contents of Cell A2 are edited (changed), the values in the Data Validation dropdown menus in Cells A10, A15, and A20 are set to the default "Select"
function onEdit(e) {
var ss = SpreadsheetApp.getActive()
var sheet = SpreadsheetApp.getActiveSheet()
var cell = sheet.getRange('A2')
var cellContent = cell.getValue()
if(cellContent === (edit) {
SpreadsheetApp.getActiveSheet().getRange('A10').setValue('Select');
SpreadsheetApp.getActiveSheet().getRange('A15').setValue('Select');
SpreadsheetApp.getActiveSheet().getRange('A20').setValue('Select');
}
}
Simple triggers should not be declared as local functions of another function, they should be declared at the global scope.
In other words, don't put onOpen and onEdit inside of myFunction.
A function on the global scope in a Google Apps Script script looks like this:
NOTE: Only one code line is included inside onOpen code block {} for simplicity. It could have any number of code lines that takes no more than 30 seconds to execute.
By the other hand simple triggers has several limitations so maybe instead of simple triggers you should consider to use installable triggers. To learn about Google Apps Script triggers please read https://developers.google.com/apps-script/guides/triggers
Also, you should bear in mind the real-time collaboration features of Google Sheets. If one user has opened the spreadsheet and another user open the same spreadsheet, the onOpen, simple and installable triggers, will be triggered and could change what the first user already edited.
After some testing I was able to make it work. As Ruben said, onOpen, onEdit cannot be inside any other function. These specify already an action so when an action of onOpen or onEdit the script will be running automatically when the spreadsheet is opened or edited.
You can check more information about Apps Script Triggers https://developers.google.com/apps-script/guides/triggers but in this specific case onEdit will run every time any cell is updated unless you specify the cell you want to run the script by using Event Objects for more information check https://developers.google.com/apps-script/guides/triggers/events.
For this specific scenario since you want the script to run when the cell is updated you have to check if that cell is being updated by using e.range.getA1Notation() the getA1Notation() returns the range in A1 notation for more information check https://developers.google.com/apps-script/reference/spreadsheet/range#getA1Notation() there is an example that will make you understand the following script.
function onOpen(e){
var app = SpreadsheetApp.getActiveSpreadsheet();
var sheet = app.getActiveSheet();
sheet.getRange("A2:A2").clearContent();
sheet.getRange("A10:A10").setValue("Select");
sheet.getRange("A15:A15").setValue("Select");
sheet.getRange("A20:A20").setValue("Select");
}
function onEdit(e){
var app = SpreadsheetApp.getActiveSpreadsheet();
var sheet = app.getActiveSheet();
var A2 = sheet.getRange("A2:A2");
console.log(e.range.getA1Notation());
if (e.range.getA1Notation() === "A2"){
console.log("A2 Updated");
sheet.getRange("A10:A10").setValue('Select');
sheet.getRange("A15:A15").setValue('Select');
sheet.getRange("A20:A20").setValue('Select');
}
}
the onOpen function will be running every time the spreadsheet is opened, and the onEdit function will be executed every time the cell "A2" changes its value. Basically you are checking if the range with A1 Notation is equals to "A2" then it will run the code within the if statement, otherwise the function will be executed but will take no action since the cell A2 is still without any change. By doing this I am not sure if there is a limit of this kind of executions but that is something you can investigate further, I don't think it will affect since this should be running on the client side not on a server side.
I hope that helps, greetings.
I wrote up some javascript code to export an excel grid that would only export the records filtered on the page by the filter tab and not the total value of records brought back. So for example if the record pulls back 23 records but you only have the view set to 10 you'll only get 10 records exported. I'm loading this function on the grid initialization as I usually do, however, for some reason it always seems to print out an empty sheet first with just the header rows, followed by the actual sheet I need. So the code is working but I can't figure out how to stop the first empty sheet from downloading as well. I don't believe I'm calling the method twice and there's definitely no looping logic... I've read over the Telerik documentation and I figured I'd come to stackoverflow since the Telerik support team just seems to refer everyone back to the same documentation
excelExport: function (e) {
var items = grid.dataSource.pageSize();
sheet = e.workbook.sheets[0];
var newSheet = sheet.rows.splice(0, items + 1) // +1 is for the header row
sheet.rows = newSheet
},
I'm trying to program a spreadsheet so that whenever I add a new entry to it, it copies the data from that cell into another spreadsheet. Here's my code (that doesn't seem to work):
function onEdit(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var target = SpreadsheetApp.openById("*************");
var target_sheet = target.getSheetByName("Dashboard");
var last_row = target_sheet.getLastRow();
target_sheet.insertRowAfter(last_row);
var target_range = target_sheet.getRange("A"+(last_row+1)+":G"+(last_row+1));
e.range.copyTo(target_range);
}
Any ideas what I'm doing wrong?
There are two types of triggers in Apps Scripts - simple triggers and installable triggers.
onEdit() is an example of a simple trigger (the name is implicit). And there are limits to what you can do inside a simple trigger. And that includes opening another spreadsheet.
So, coming to the solution.
Rename your function to something else, say onEdit1() . And set up an installable trigger manually and set it to run each time the spreadsheet is edited.
Hint: Use Resources --> Current project's triggers from the script editor