I'm using Google App Script.
I've created drop down boxes from values in a Google Sheet. My intent is to collect the selectedIndex(es) in an array, send them to the back end as parameters, and have them output a value from the same Google Sheet based on an argument.
I'm able to successfully pass the array of selected indexes of the drop down boxes from the html form to the back end (code.gs), but when I try to get the value of a cell in Google Sheets using getRange & selectedindexes, I always get these errors:
console.log - Uncaught at calculateCL (Code:22)
Logger.log - is able to log the value of the cell but when I "return PTvalue", the front end logs it as undefined.
Please help!
page.html
//gets selected index from dropdown
var CountryIndex = document.getElementById("menu").selectedIndex;
var CUTypeIndex = document.getElementById("ListCUTyp").selectedIndex;
//pushes indices into array
formArray = [];
formArray.push(CountryIndex);
formArray.push(CUTypeIndex);
//array sent to back end within function calculateCL
//on success of calculateCL, run function CLOutput
google.script.run.withSuccessHandler(CLOutput()).calculateCL(formArray)
//outputs value from GS
function CLOutput(PTvalue){
console.log(PTvalue);
document.getElementById("ListPT").value = PTvalue;
}
code.gs
//gets value of cell based on selected index array
function calculateCL(formArray) {
var PTvalue = sheet.getRange(2+formArray[0], 2+formArray[1]).getValue();
Logger.log(PTvalue);
return PTvalue;
}
Your calculateCL has parameter but seems like you are using it without a parameter inside.
google.script.run.withSuccessHandler(CLOutput()).`calculateCL()`
Take a look at your function:
function calculateCL(formArray)
This is incorrect:
google.script.run
.withSuccessHandler(CLOutput())
.calculateCL(formArray);
This is a correct form:
google.script.run
.withSuccessHandler(CLOutput)
.calculateCL(formArray)
function CLOutput(PTvalue){
console.log(PTvalue);
document.getElementById("ListPT").value = PTvalue;
}
This is also correct
google.script.run
.withSuccessHandler(function(PTValue){
console.log(PTvalue);
document.getElementById("ListPT").value = PTvalue;
})
.calculateCL(formArray)
Related
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).
I am a beginner and using $.get to retrieve data from a rest API such as:
[{"id":"1","url":"http:\/\/123.456.78.910\/workforce\/images\/item1.jpg","price":"99","description":"Mobile Phone"},
{"id":"2","url":"http:\/\/123.456.78.910\/workforce\/images\/item2.jpg","price":"98","description":"Laptop"}
{"id":"3","url":"http:\/\/123.456.78.910\/workforce\/images\/item3.jpg","price":"92","description":"Console"}] }
$.get('http://xxxxxxxxxxx,
function (data) {
var obj = $.parseJSON(data);
So from what I understand I have retrieved the data from the REST API and parsed it so it is stored in a variable called obj.
My question is, how do I access and use each unique record in the obj variable?
Each record has it's own picture (item1.jpg, item2.jpg etc).
Whem my app loads I want it to show the item1.jpg image, and I want to be able to navigate to the other item pictures using buttons (previous / next).
I also want the description and price to be displayed underneath in some text input fields.
What I have figured so far is that I should:
Iterate through the obj variable, and store each record into an array.
Upon app initialisation I can set the default value for the image placeholder to array[index0].url, and set the description and price fields.
I can then set the previous and next buttons to array[currentIndex-1] or array[currentIndex+1].
Would this be the best way to do it?
Or can I just do this without using an array and manipulate the obj.data directly?
Thanks!!!
I may not be understanding what exactly what you want to do but I think I have the gist. If you just want to show the picture then the array of just images probably wouldn't be a bad idea. However, it looks like the Jason you're getting is already in an array. You can just use array index notation to get to what you want.
ie)
var arr = //your json response ;
var current = 0; //sets currently displayed object to the first in the array
var setCurrent = function () {
var image = arr[current]["url"];
}
You can then modify current however you want (on click on arrow iterate up/down, etc) then call the setCurrent function to set your image the the one you want. Hope that helps!
You can use the response you have from $.get() directly.
It is an array of objects.
You can use it like this:
console.log(data[2].description);
// outputs: "Console"
I've made a CodePen demo where it has a 4th object with a real image url to show you how to use the url info...
EDIT
Just in case you wouldn't know this:
You can use the response inside the scope of the $.get() callback...
You can not use it straith after the $.get() outside the callback since $.get() is asynchronous.
You can use it in some other handler wich will happen after the response is received.
var getResponse;
$.get('http://xxxxxxxxxxx', function (data) {
getResponse = data;
console.log(data[2].description);
// outputs: "Console"
console.log(getResponse[2].description);
// outputs: "Console"
});
console.log(getResponse[2].description);
// outputs: "Undefined"
// But since this handler will be triggered long after the response is obtained:
$("#somebutton").click(function(){
console.log(getResponse[2].description);
// outputs: "console"
});
In order for your page javascript to be able to access the data retrieved from your ajax request, you'll need to assign it to some variable which exists outside the callback function.
You will need to wait until the ajax request has been processed before you can read the array. So you might want to set the actual default image to be something that doesn't rely on the ajax request (a local image).
Here's a simple approach
// fake testing ajax func
function fakeget (url, callback) {
setTimeout(callback(JSON.stringify([
{"id":"1","url":"http:\/\/123.456.78.910\/workforce\/images\/item1.jpg","price":"99","description":"Mobile Phone"}, {"id":"2","url":"http:\/\/123.456.78.910\/workforce\/images\/item2.jpg","price":"98","description":"Laptop"},
{"id":"3","url":"http:\/\/123.456.78.910\/workforce\/images\/item3.jpg","price":"92","description":"Console"}
])), 1000);
}
// real code starts here
// global variables for ajax callback and setImg func to update
var imageData, currentImg;
// change this back to $.get for real
fakeget('http://xxxxxxxxxxx',
function (data) {
imageData = $.parseJSON(data);
setImg(0);
}
);
function setImg(index) {
// turns negative indices into expected "wraparound" index
currentImg = (index % imageData.length + imageData.length) % imageData.length;
var r = imageData[currentImg];
$("#theImg").attr('src', r.url);
$('#theDescription').text(r.price + " " + r.description);
}
$("#prev").click(function () {
setImg(currentImg - 1);
});
$("#next").click(function () {
setImg(currentImg + 1);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<img id='theImg' src='somedefault.jpg'>
<div id='theDescription'></div>
</div>
<button id='prev'>Prev</button>
<button id='next'>Next</button>
Few observations :
Your JSON Object is not a valid JSON.
No need to parse it again your data is already a JSON Object.
Working fiddle
var data = [{"id":"1","url":"http:\/\/123.456.78.910\/workforce\/images\/item1.jpg","price":"99","description":"Mobile Phone"},{"id":"2","url":"http:\/\/123.456.78.910\/workforce\/images\/item2.jpg","price":"98","description":"Laptop"}, {"id":"3","url":"http:\/\/123.456.78.910\/workforce\/images\/item3.jpg","price":"92","description":"Console"}];
for (var i in data) {
var imgUrl = data[i].url;
console.log(imgUrl);
}
I have a function in Google Forms script to add a Unique ID to the row being added to the associated Sheet. This function is triggered on submission of the form.
Here's one version of the script I've been playing around with:-
function onFormSubmit(e) {
// Get the active sheet
var sheet = SpreadsheetApp.getActiveSheet();
// Get the active row
var row = sheet.getActiveCell().getRowIndex();
// Get the next ID value.
var id = sheet.getRange("Z4").getValue();
// Check of ID column is empty
if (sheet.getRange(row, 1).getValue() == "") {
// Set new ID value
sheet.getRange(row, 1).setValue(id);
sheet.getRange("Z4").setValue("Z4"+1)
}
}
On debugging I get the message:-
TypeError: Cannot call method "getActiveCell" of null. (line 5, file "CreateID")
I've tried cropping the code right down to just a simple setValue, and I get the same issue - "cannot call method...", with pretty much every line except the getActiveSheet.
The trigger of the function works ok, as I get notifications to say that the function itself had failed to execute successfully. I've looked online, tried a few things, but can't find a solution as yet.
So what I'm really after is the correct method of accessing the spreadsheet that the form is posting to. If SpreadsheetApp.getActiveSheet() isn't the right method, what is?
Totally new to this script, last programmed in PLI(!), sorry. Any pointers to existing solutions or other, would be appreciated.
When a form is submitted, the trigger function has no active spreadsheet, nor will it have an active cell. (It's not associated with a Sheets UI, so those concepts are meaningless.)
However, the event parameter, e, will provide information about the row that has been added by the Form. See Google Sheets events:
e.range contains a Range object covering the cells that have been filled by the form submission that triggered your function. You can backtrack from there to get the sheet.
sheet = e.range.getSheet();
You function becomes something like this:
function onFormSubmit(e) {
// Get the active sheet
var sheet = e.range.getSheet();
// Get the active row
var row = e.range.getRowIndex();
// Get the next ID value.
var id = sheet.getRange("Z4").getValue();
// Check of ID column is empty
if (sheet.getRange(row, 1).getValue() == "") {
// Set new ID value
sheet.getRange(row, 1).setValue(id);
sheet.getRange("Z4").setValue("Z4"+1)
}
}
Now, you have other problems to deal with.
sheet.getRange(row, 1).getValue() is not necessary; that value has just been handed to you as e.values[0].
However, on a form submission, the first column contains a Timestamp, so it won't ever be empty. If your "ID" value is the first question on the form, then it's actually in column 2, or e.values[1].
The cell Z4 will likely move on you, as form submissions insert new rows into the sheet. It would be better to pull that value from a different sheet - or better yet use the Properties Service to manage it.
To make use of the event, you'll need to simulate it for testing. Read over How can I test a trigger function in GAS?.
You need to check whether getActiveSheet() succeeded.
function onFormSubmit(e) {
// Get the active sheet
var sheet = SpreadsheetApp.getActiveSheet();
if (!sheet) { // No active sheet, nothing to do
return;
}
// Get the active row
var row = sheet.getActiveCell().getRowIndex();
// Get the next ID value.
var id = sheet.getRange("Z4").getValue();
// Check of ID column is empty
if (sheet.getRange(row, 1).getValue() == "") {
// Set new ID value
sheet.getRange(row, 1).setValue(id);
sheet.getRange("Z4").setValue("Z4"+1)
}
}
I'm struggling with following issue:
I have a spreadsheet responsible for tracking my stock market investments. It calls external service to get a CSV with current prices.
So, there is a function customFunction() that calls UrlFetchApp and returns current price of an item. I'd like it to add a note to the cell that it has been called from, containing current datetime. Something like this:
function customFunction() {
//get price from csv
var url = "http://sth.com";
var csv = UrlFetchApp.fetch(url);
var result = Utilities.parseCsv(csv);
var currentValue = parseFloat(result[1][6]);
//set note
SpreadsheetApp.getActiveSheet().getActiveCell().setNote("Hey, I'm the note with the datetime");
//return value so it can be set as cell value
return currentValue;
}
I am calling this function from cell E21 (value =customFunction()).
And so everything works as planned, except the line that supposed to set a note. I am sure I get correct cell (tested by returning A1notation as a value). I get the error:
error: You do not have permission to call setNote
Does anybody know if it's possible to set a note to the cell that calls a custom function from that function? Or maybe you have any idea of workaround, like catching the moment that function reloades with new data and using it as a trigger?
your problem is due to permission limitations:https://developers.google.com/apps-script/execution_custom_functions#permissions
the function SpreadsheetApp.getActiveSheet().getActiveCell() is not anonymous and can't be used here.
Unfortunately I don't know any efficient workaround
How would I do the question asked above. I have tried .append() in javascript but can you get data from one html file and insert it into another?? Some please help.
If the page you are receiving the data was created by your js then do it like this.
var childPage = window.open("somepage.html");
The child page would need a global function to receive data, then just call it.
childPage.passData(dataToPass);
If the page to receive the data is the parent, and the input is on the child do like this.
window.parent.someFunction(dataToPass);
Your respective functions would then have to take said data and do the work fro there.
the functions do have to be on the global scope of each page.
Your should wrap the inputs in a<form> whose action attribute is set to the url of the page in which you want to display the values, as shown below:
<form action='url to second page' method='get'>
<input name='name' value='something' />
<button>Submit</button>
</form>
In the second html page, You can retrieve the request parameters by calling the js function given in this answer when it is loaded:
For example,
<html>
<head>
<title>b.html</title>
<script>
function load() {
var params = getRequests();
console.log(params['name']);
}
function getRequests() {
var s1 = location.search.substring(1, location.search.length).split('&'),
r = {}, s2, i;
for (i = 0; i < s1.length; i += 1) {
s2 = s1[i].split('=');
r[decodeURIComponent(s2[0]).toLowerCase()] = decodeURIComponent(s2[1]);
}
return r;
};
</script>
</head>
<body onload='load();'></body>
</html>
The function getRequests() returns an object containing all request parameters with the name of input element as key value. So if your first html page contains an input with name='test', the following code :
var params= getRequests();
var value =params['name'];
will give you the value of test input in second html page. Then you can use DOM API methods such as document.getElementById() to target the table elements in which you want to display the value, and set it's innerText.
can you get data from one html file and insert it into another?
Try .load()
$("#mydivid").load("/myotherpage.html");
To get a specific part of that page
$("#mydivid").load("/myotherpage.html #dividonotherpage");
We can also do something after it has loaded
$("#mydivid").load("/myotherpage.html", function() {
$("#mydivid").show();
/* like grab the values of attributes .. */
});
https://api.jquery.com/load/
edit: / reading #QBM5, I see you might be referring to 'data' as local client side user input from another window. Disregard this answer if so, as this will not pick up changes that are not set as part of the original delivered markup.