InstallableTriger doesn't fire when using setValue() [duplicate] - javascript

This question already has an answer here:
Trigger onEdit() with a programmatic edit
(1 answer)
Closed 3 years ago.
I'm currently working on a GAS project, and facing a strange issue.
I've set up an installable trigger that detect any change in the column A, and launch a custom function (installableOnEdit). In case the row in which the modification has been done contains datas, I call a custom function (GetProductInfo) to make an API call that will write value in different cell.
If not I just clear some other rows.
My issue is, if I manually insert datas in the A column, then it works like a charm, but in case i'm using a function to write in the A column with the .setValue (from an other custom function), the trigger does not fire.
Here is my code :
function installableOnEdit(e) {
var ss = SpreadsheetApp.getActive();
var editedRange = e.source.getActiveRange();
var editedRow = editedRange.getRow();
var editedCol = editedRange.getColumn();
var url = editedRange.getValue();
var urlColumn = 1;
if (editedCol == urlColumn){
if (url != ""){
GetProductInfo(url, "material", editedRow, editedCol);
}else {
//Reset material
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Data").getRange(editedRow, editedCol+3).setValue("");
}
}
}
Any ideas ?

Thanks to tehhowch i've been able to find a solution.
That's not gonna fit for everyone, but instead to use the trigger, i simply call my custom function that retrieve the information from the API (GetProductInfo), by calling it at the end of the function that make the .setValue().

Related

Problem with connecting Google app script to my google sheet

I have been trying to get the time stamp in my google sheet every time I make an entry in the first column it should give me the date and time stamp in the fourth column. However, I don't know what I'm doing wrong all the tutorials are a year old and Google has removed the edit script feature from google Sheets' "tools" menu so I don't know how to connect the function to it properly.
Here's the code :
function onEdit(e) {
var row = e.range.getRow();
var col = e.range.getColoumn();
if(col===1 && row>1 && e.source.getActiveSheet.getName()=== "Dummy1") {
e.source.getActiveSheet.getRange(row,4).setValue(new Date());
}
}
And this is the error it gives me:
TypeError: Cannot read property 'range' of undefined
onEdit # TimeStamp.gs:3
And a link of the sheet: https://docs.google.com/spreadsheets/d/1lBe7GDT7-yAS3GiBhoiVppIZe7gDkI3ihExmo5ZaEy8/edit?usp=sharing
You have a typo. It is getColumn() not getColoumn().
Also getActiveSheet is a function, so it needs parenthesis getActiveSheet().
function onEdit(e) {
var row = e.range.getRow();
var col = e.range.getColumn();
if(col===1 && row>1 && e.source.getActiveSheet().getName()=== "Dummy1") {
e.source.getActiveSheet().getRange(row,4).setValue(new Date());
}
}
This solution takes advantage of the event object much better and will run much faster.
function onEdit(e) {
//e.source.toast('Entry');//these lines can be an aid to debugging
//Logger.log(JSON.stringify(e));//uncomment this to learn more about event object
const sh = e.range.getSheet();
if (e.range.columnStart == 1 && e.range.rowStart > 1 && sh.getName() == "Dummy1") {
e.range.offset(0, 3).setValue(new Date());
}
}
You cannot run functions like this from the script editor because they require the event object from the onedit trigger which is generated when you edit a cell on the spreadsheet. So to test them you simply need to create them and save them as function onEdit(e) and then edit the page to see it work or not. You can utilize e.source.toast('flags') in different locations to help you to determine where problems exist in the function.

OnChange Trigger that will fire when there's an update on a specified column

I have 2 Spreadsheets, namely Source and Destination. In Source, I have listed my sample products and their designated prices and are copied over to Destination using IMPORTRANGE function. Now, I'm trying to fire the OnChange trigger when there is an update on 'Destination Sheet 1'B:B only. Here's my code:
function testFunction(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Destination Sheet 1");
if (e.source.getSheetName = "Destination Sheet 1" && e.range.getRow() > 1 && e.range.getColumn() == 2){
var range = ss.getRange(e.range.getRow(), 3, ss.getLastRow());
range.setValue("There's an update");
}
}
I've also added the function testFunction on OnChange Trigger.
However, based on Event Objects Documentation there's no option for me to get the range that was updated from the events object. By "range", I mean the Destination Sheet 1'B:B. I'm getting this error:
"TypeError: Cannot read property 'getRow' of undefined at testFunction(Code:3:64)"
Desired Outcome (After an update on the pricing in 'Destination Sheet 1'!B:B):
I'm still fairly new to google scripts and I've really tried everything that I can think of but it is not getting me anywhere. Any help is greatly appreciated. Thank you in advance!
UPDATE 1:
It seems that there is no way of triggering the OnChange trigger when there is an update on a sheet that is on IMPORTRANGE function.
ALTERNATIVE:
Since it doesn't work, I was thinking of using OnEdit trigger instead on Source to handle that edit and apply my desired outcome on Destination. However, I'm having permission issues.
Exception: You do not have permission to call SpreadsheetApp.openById. Required permissions: https://www.googleapis.com/auth/spreadsheets
Here's my code:
function onEdit(e){
aSourceFunction(e);
}
function aSourceFunction(e) {
//IF stmt to make sure that OnEdit Trigger is not triggerred anywhere
if (e.source.getSheetName = "Source Sheet 1" && e.range.getRow() > 1 && e.range.getColumn() == 2){
//my attempt to call Destination Sheet 1 from Destination
var ss = SpreadsheetApp
.openByID("1QMtU6VbNQyDLXMHmaLBBDePH5l")
.setActiveSheet("Destination Sheet 1");
//desired outcome
ss.getRange(e.range.getRow(), 4, ss.getLastRow()).setValue("There's an update");
}
}
Also, I'm not sure if my concern is related but I've read somewhere that my code shouldn't have #OnlyCurrentDoc but I don't have that anywhere in my codes even in my Manifest file.
{
"timeZone": "Asia/Hong_Kong",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8"
}
Here's the scope of it:
Update 2: It worked! Initially, I have a simple OnEdit trigger. However, according to this, you need to have an installable OnEdit trigger for it to work (also fixed my code for the errors) and when I ran the code, the "permission prompt" showed up for me to access the Destination Spreadsheet. Here's my code for anyone who needs it:
function aSourceFunction(e) {
//IF stmt to make sure that OnEdit Trigger is not triggerred anywhere
if (e.source.getSheetName = "Source Sheet 1" && e.range.getRow() > 1 && e.range.getColumn() == 2){
//my attempt to call Destination Sheet 1 from Destination
var ss = SpreadsheetApp.openById("1QMtU6VbNQyDLXMHmaLBBDePH5l-4wDPUNQ827gb3jXY");
var sheet = ss.getSheetByName("Destination Sheet 1");
//desired outcome
sheet.getRange(e.range.getRow(), 4).setValue("There's an update");
}
}
Note: Go to Edit>Current project's trigger>Add Trigger using aSourceFunction as your function and OnEdit as the trigger.
From the question
However, based on Event Objects Documentation there's no option for me to get the range that was updated from the events object. By "range", I mean the Destination Sheet 1'B:B.
You are right, there is no way to get the range that was udpdated from the change event object.
But if you already know that range of interest is 'Destination Sheet 1'!B:B you can use:
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var range = spreadsheet.getRange("Destination Sheet 1!B:B");
Regarding the error message it occurs because the change event object doesn't include a range property.
Please bear in mind that (from Installable Triggers)
An installable change trigger runs when a user modifies the structure of a spreadsheet itself—for example, by adding a new sheet or removing a column.
Regarding how to make the OnChange trigger to fire on updates on Column B only, you can try to use getActiveRange() but sometimes it doesn't work. There is an open issue related to this getActiveRange() incorrect in onChange trigger for some column/row operations
Related
How to identify changed cell in onChange function
Why Class Range getValues sometimes returns [[]] when chained to Class Sheet getActiveRange?

Max number of script in a spreadsheet [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
In my spreadsheet, I have inserted 35 project in GAS to generate google doc from the same sheet with the placeholder present in the docs.
The script takes the fields from each row in the sheet the script is contained in and using a Google Doc template (identified by TEMPLATE_ID) creates a PDF doc. The fields replace the place-holders in the template. The place-holders are identified by having a % either side, e.g. %Name%. It is invoked by the “Create PDFs” menu.
Naturally I have created a project for each Google Doc template (because I need 35 different doc from 35 different templates) and so in my sheet i have about 35 custom voice in menu that activate each script. Sometimes some scripts not appears all together and so I must to reload the sheet to find the script I need.
Why? There is a way to avoid this?
The script is this:
var TEMPLATE_ID = 'xxxxxxxx';
var PDF_FILE_NAME = '';
var RESULTS_FOLDER_ID = 'xxxxxxx';
var SENT_COLUMN_NAME = 'Date Sent';
var FILE_NAME_COLUMN_NAME = 'File Name';
var EMAIL_COLUMN_NAME = 'Email';
var DATE_FORMAT = 'yyyy/MM/dd';
var DATE_TIME_FORMAT = 'yyyy/MM/dd HH:mm:ss';
function sendMultiplePdfs() {
var ui = SpreadsheetApp.getUi();
if (TEMPLATE_ID === '') {
throw new Error('TEMPLATE_ID needs to be defined in Code.gs');
return;
}
var templateFile = DriveApp.getFileById(TEMPLATE_ID);
var activeSheet = SpreadsheetApp.getActiveSheet();
var ranges = activeSheet.getActiveRangeList().getRanges();
var activeRows = [];
ranges.forEach(function(range) {
activeRows.push(range.getValues());
})
Update:
Solved by replacing the Template ID taking the value dynamically from a cell:
var ss = SpreadsheetApp.openById("ID_OF_THE_SS").getSheetByName("SHEET_NAME");
var TEMPLATE_ID = ss.getRange("RANGE_OF_THE_ID").getValue();
AFAIK there isn't a max number of projects that can be bounded to a G Suite editor document but having simple triggers on them could cause problems due to a "race - condition"
Rhetoric question
If there are multiple onOpen each of them creating a custom menu, will all of them be displayed correctly always on the same order?
One approach is to "parameterize" your project, i.e. create 35 functions to call a parameterized function receiving the template id and other parameters
Those 35 functions could be called from a menu, from images, among other ways to call them.
In Google Apps Script / JavaScript a function looks like this:
function doSomething(){
console.info('Hello world!');
}
the parameterized version could be
function saySomething(message){
console.info(message);
}
Then you call saySomething by passing a value for the parameter
function greetTheWorld(){
var message = 'Hello world!';
saySomethin(message);
}
In the case of this project you might put all the global variables in an array of object, having one object for each template.
var settings = [
{ /* First template */
TEMPLATE_ID:'xxxxxxxx',
PDF_FILE_NAME:'',
RESULTS_FOLDER_ID:'xxxxxxx',
SENT_COLUMN_NAME:'Date Sent',
FILE_NAME_COLUMN_NAME:'File Name',
EMAIL_COLUMN_NAME:'Email',
DATE_FORMAT:'yyyy/MM/dd',
DATE_TIME_FORMAT:'yyyy/MM/dd HH:mm:ss'
},
{ /* Second template */
TEMPLATE_ID:'xxxxxxxx',
PDF_FILE_NAME:'',
RESULTS_FOLDER_ID:'xxxxxxx',
SENT_COLUMN_NAME:'Date Sent',
FILE_NAME_COLUMN_NAME:'File Name',
EMAIL_COLUMN_NAME:'Email',
DATE_FORMAT:'yyyy/MM/dd',
DATE_TIME_FORMAT:'yyyy/MM/dd HH:mm:ss'
}
]
The pass the corresponding object of the parameterized function which previously was prepared to receive the corresponding values as an object.
Reference
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions

How to pull a specific row from one spreadsheet to another, update it and then replace it on the original sheet

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).

Multiple instances of CKEditor (in Safari) [duplicate]

This question already has answers here:
CKEditor instance already exists
(32 answers)
Closed 9 years ago.
I'm having a problem creating multiple instances of a CKEditor in a JQuery UI dialog. The dialog loads a remote form via AJAX, so the goal is to be able to close and reopen the dialog and have a new instance of the editor. With the default options, when reopening the dialog it gives an error saying that an editor with that name already exists. So I have tried several methods of destroying the editor instance and they all result in the same problem. When the editor is reloaded, the text area says null and the buttons don't function.
Currently I'm using this method of destroying the instance:
var instance = CKEDITOR.instances['test'];
if (instance) { CKEDITOR.remove(CKEDITOR.instances['test']); }
I recreated the issue with a couple of simple html files available for download here.
EDIT: I just tried using two remote files with a text area that has a different name and I have the same problem. When one dialog is opened and then closed, the other dialog has a "null" CKEditor when it is opened.
Also, apparently this is only a problem in Safari.
this is what i've done:
var CKeditors = {};
function loadEditors() {
var $editors = $("textarea.ckeditor");
if ($editors.length) {
$editors.each(function() {
var editorID = $(this).attr("id");
if(CKeditors[editorID]){
CKeditors[editorID].destroy();
CKeditors[editorID] = null;
}
var dst = editorID+'-element';
var html = '';
if( $(this).val() ){
html = $(this).val();
}
CKeditors[editorID] = CKEDITOR.appendTo(dst, {}, html);
});
$("textarea.ckeditor").hide();
}
}
function updateCKEditors() {
for(x in CKeditors){
$("#"+x).val(CKeditors[x].getData());
}
}
then after ajax succes im doing
loadEditors()
and before form submitting (for example using ajax):
updateCKEditors()
you need jQuery to have that working. this is for zend_forms, but after few corrections should work in normal forms too. play with 'dst' to do that.
Bit of an old topic, but i had a similar problem.
I used activ's solution above, which worked out great!
CKEDITOR.appendTo did't work out for me, but with the next slight modification to the loadEditors function it did:
function loadEditors() {
var $editors = $("textarea.ckeditor");
if ($editors.length) {
$editors.each(function() {
var editorID = $(this).attr("id");
if(CKeditors[editorID]){
CKeditors[editorID].destroy();
CKeditors[editorID] = null;
}
var dst = editorID+'-element';
CKeditors[editorID] = CKEDITOR.replace(dst, {});
});
}
}
what I do:
var instance = CKEDITOR.instances['test'];
instance.destroy();
instance = null;

Categories