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
Related
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)
I'm trying to make a modal popup that does some work on a given table cell within a Google Doc using HTMLService and GAS.
The popup works and in general I can pass data from the server script to the client script and back. The piece of data I can't seem to pass is which table cell the cursor was at when the popup was opened.
Approach 1: If I pass it directly to client and back, the reference is broken, because it's null by the time it gets to the client. I can pass the cell's contents just fine in the exact same context, so strings work, it's just the fact that it's a cell reference. (Makes sense enough.)
Approach 2: If I store the TableCell reference in a global variable on the server side, I get Cannot call method "getText" of undefined--the reference is undefined after the first round trip. (I guess the server script gets reloaded entirely in that time.)
Approach 3: If I use CacheService.getUserCache() on the client side, when I try to get my cached TableCell object, it seems disconnected from the one I had set, because I get the heretofore unGoogleable error Cannot find function getText in object TableCell.
// server, before popup is opened:
cache.put('cell_currently_being_edited', active_doc.getCursor().getElement().getParent());
// popup client calls server function like so:
google.script.run.withSuccessHandler(load_content).get_starting_content();
// server, where the above error occurs:
function get_starting_content() {
var cell_currently_being_edited = cache.get('cell_currently_being_edited');
return cell_currently_being_edited.getText();
}
If cells had some sort of fixed ID value I could pass, that could work...
I would do it based on the text in the cell, but I hope that's not the only option, because cell text in general (and yes, in this specific context too) may not be unique, so after the round trip I may end up replacing text in some other cell than the cursor was in when the user activates the popup.
Solution:
You could use the cell index as unique identifier to pass the RC string back and forth between client and server as string values are legal.
Use Properties/Cache Service to store RC Index information as string for future retrieval in Server, If needed.
Sample Script:
function getRCTIndexOfTable(tableCell) {
var tableRow = tableCell.getParentRow();
var table = tableRow.getParentTable();
var column = tableRow.getChildIndex(tableCell);
var row = table.getChildIndex(tableRow);
var body = table.getParent().asBody();
var tableId = body.getChildIndex(table);
return 'R' + row + 'C' + column + 'T' + tableId;
}
function main() {
var active_doc = DocumentApp.getActiveDocument();
var activeTableCell = active_doc
.getCursor()
.getElement()
.getParent()
.asTableCell();
Logger.log(getRCTIndexOfTable(activeTableCell));
}
References:
TableCell
TableCell from RCIndex
Legal Values
PropertiesService
CacheService
Since the other answer (even after modifying the code) focused on the "saving" half of the problem, I thought I would share code that includes how to load the reference afterwards to complete the round trip:
// save
var table_cell = active_doc.getCursor().getElement().getParent();
var table_row = table_cell.getParentRow();
var table = table_row.getParentTable();
var column_index = table_row.getChildIndex(table_cell);
var table_index = active_doc.getBody().getChildIndex(table);
var row_index = table.getChildIndex(table_row);
cache.put('table_currently_being_edited', table_index);
cache.put('row_currently_being_edited', row_index);
cache.put('column_currently_being_edited', column_index);
// load
function get_cell_currently_being_edited() {
var table_index = cache.get('table_currently_being_edited');
var row_index = cache.get('row_currently_being_edited');
var column_index = cache.get('column_currently_being_edited');
return active_doc.getBody().getChild(table_index).getCell(row_index, column_index);
}
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 am using algolia javascript api for retrieving all records in my index using browse function, but still it is returning 1000 records. Here is my code:
function load_location_list(){
var client = algoliasearch('ID', 'KEY');
var index_name = "locations_new";
var attribute_list = "*";
var index = client.initIndex(index_name);
index.browse({
"attributesToRetrieve": attribute_list,
}).then(function search_Success(response) {
console.log(response);
});
}
Actually, browse doesn't return more than 1000 elements at the first call. However, the response contains a cursor that you can use to access the next elements with the browseFrom function.
However, the previous method is kind of manual. You probably want to use the browseAll function instead which lets you access all the elements sequentially.
You can find more informations about all the browse* functions in the README of the JS client (also available in the Algolia documentation).
Very confused here.
I have a search box which reads a list of school names from my database. When I select a school, the id (from the db) gets put in a hidden textbox.
I also have a search box which reads a list of courses from my database. However, I made the query so that it only reads the courses from the selected school.
It does that, in theory.
I was planning to pass the school id, which I grab from the hidden box, to the search script which in turn passes it to my database query. However, the variable I put my school id in doesn't seem to be updating.. yet it does. Let me explain.
I come on the page. The school for my test account has id 1. The id number in my hidden box is indeed 1. I search for a school which I know has some courses assigned to it: the id number in the box changes to 3.
I have a JS variable called school_id which I declared outside of my $(document).ready. I assume that means it's global (that's what I got taught even though SO told me once it isn't really the correct way to do this. Still have to look into that). I wrote a function which updates this variable when the school search box loses focus:
$("#school").blur(function() {
school_id = $("#school_id").val();
});
A quick javascript:alert(school_id); in my browser bar also shows the updated variable: it is now 3 instead of 1.
Onto the search script part of my page (excerpt of the script):
script:"/profiel/search_richting?json=true&limit=6&id=" + school_id + "&"
As you can see, I pass the school_id variable to the script here. However, what seems to be happening is that it always passes '1', the default variable when the page loads. It simply ignores the updated variable. Does this string get parsed when the page loads? In other words, as soon as the page loads, does it actually say &id=1? That's the only idea I can come up with why it would always pass '1'.
Is there a way to make this variable update in my script string? Or what would be the best way to solve this? I'm probably missing out on something very simple here again, as usual. Thanks a lot.
EDIT
Updated per request. I added a function getTheString as was suggest and I use the value of this function to get the URL. Still doesn't work though, it still seems to be concatenating before I get a chance to update the var. HOWEVER, with this code, my ajax log says id:[object HTMLInputElement], instead of id:1. Not sure what that means.
<script type="text/javascript">
var school_id;
$(document).ready(function() {
$("#school").blur(function() {
school_id = $("#school_id").val();
});
// zoekfunctie
var scholen = {
script:"/profiel/search_school?json=true&limit=6&",
varname:"input",
json:true,
shownoresults:false,
maxresults:6,
callback: function (obj) { document.getElementById('school_id').value = obj.id; }
};
var as_json = new bsn.AutoSuggest('school', scholen);
var richtingen = {
script: getTheString(),
varname:"input",
json:true,
shownoresults:true,
maxresults:6
};
var as_json2 = new bsn.AutoSuggest('studierichting', richtingen);
});
function getTheString() {
return "/profiel/search_richting?json=true&limit=6&id=" + school_id + "&";
}
</script>
This is because the URL is static, it is not updated as the ID changes.
You should update the URL as part of the code you wrote to get the ID:
$("#school").blur(function() {
school_id = $("#school_id").val();
// update URL here ...
});
Aren't you concatenating script:"/profiel/search_richting?json=true&limit=6&id=" + school_id + "&" before the event is fired and the var updated?
Okay. So the problem was my third party plug-in instead of the code I wrote. I fixed this by editing the code of the autoSuggest plugin so it now includes my id field in the AJAX request.
var url = this.oP.script+this.oP.varname+"="+encodeURIComponent(this.sInp)+"&id="+ $("#school_id").val();
Thanks to everyone who tried to help me out!