How to get the text from a Google Spreadsheet cell? - google-apps-script

I'm trying to write a Google Apps script that involves getting the text from a table cell inside a Google Spreadsheet. To illustrate the situation, let's say the word "hello" is in the cell B4, and my function is supposed to get that string from that cell. How would I go about this? I've tried the following but that hasn't worked for me.
function getText() {
var body = DocumentApp.getActiveDocument().getBody();
var text = body.getText();
}
I get this error whenever I try to use the function.
Error: You do not have permission to call getActiveDocument
Even after getting authorization for the script, I still get the same error. Any ideas on how to solve this?

You say you're trying to retrieve a Spreadsheet information but uses the Document class, they're not interchangeable like that, you'll have to use Spreadsheet class.
Eg.
var allVals = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
Logger.log(allVals);

Related

How to get user input in google app script?

Here is what I want to achieve:
I want to delete 'n' number of rows from my google spreadsheet document. This 'n' can vary depending on number of wrong entries inserted in the document (I know this number before running the function). And I want to give myself a flexibility to choose this number (just like console input in C, C++ or any other languages).
Some researching shows solution via SpreadsheetApp.getUi() mode. But it is giving me error: Exception: Cannot call SpreadsheetApp.getUi() from this context.
I don't want to open my spreadsheet as it is huge in size & takes time to load. Purpose of deleting rows pragmatically is that I don't have to open it, else its all 'moo' point.
Another solution could be to just create an variable and change is manually before running script. But it could create bad data if I forget to change that variable someday (I want to make it idiot-proof).
Is there any way to get user input for standalone google app script? and without opening that particular google sheet?
You can always put the script into a blank sheet and treat it as a placeholder for your functions and have the ui prompt pop there. This way, you don't need to open your large sheet. You can always access other sheets when in another via Apps Script. This would be easier and you just need to transfer your script here.
Code:
function showPrompt() {
var ui = SpreadsheetApp.getUi();
var result = ui.prompt(
'Rows to delete?',
'Input:',
ui.ButtonSet.OK_CANCEL);
var button = result.getSelectedButton();
var numRows = result.getResponseText();
if (button == ui.Button.OK) {
// call function and pass the value
deleteSheetRows(numRows);
}
}
function deleteSheetRows(numRows) {
// url of the sheet with data
var url = "https://docs.google.com/spreadsheets/d/***************/";
var sheet = SpreadsheetApp.openByUrl(url);
// do what you need to do here for that sheet using "numRows" value
Logger.log("deleting "+numRows+" rows");
}
Output:
You can create a function in web app just write doGet() or doPost() function and call it with your input.
refer https://developers.google.com/apps-script/guides/web
Take input number of rows which is n in your case, and add your code to delete rows from SpreadSheet.
you can pass input for get by using query parameter like:
?n=4
and you can use n in doGet() method.

Populate rows with the sheets names of an spreadsheet

I have an spreadsheet that contains multiple sheets with some recipes. I'm storing the sheets' titles in the first sheet manually, to reference the recipes titles on another spreadsheet, but this doesn't scale well, so I want to automate the process. I believe I can't do that with any of the built in functions, so I'm trying to build a custom function to do it.
I already know some coding, but my experience with google API is very limited. My current attempt is returning the following exception: "You do not have the permission to call set value". After google this error, I discovered that custom functions seemingly cannot set values to another cells, so I'm here to find an alternative to such a trivial behavior.
This is my current code:
function updateSheetTitles() {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var cell = SpreadsheetApp.getActiveSpreadsheet().getCurrentCell();
var row = cell.getRow();
var column = cell.getColumn();
for (var i=0 ; i<sheets.length ; i++){
if(i<1) //For passing trought the first sheet
return;
cell.setValue(sheets[i].getSheetName);
var range = SpreadsheetApp.getActiveSheet().getRange(row+1, column, 1, 1);
cell = SpreadsheetApp.getActiveSheet().setCurrentCell(range);
}
}
And here's and image to illustrate what I want:
This is probably an easy task, but I failed to find a way to build this, so any ideas to accomplish that would be appreciated.
I believe your goal as follows.
You want to retrieve sheet names in the active Google Spreadsheet.
You want to put the sheet names to the active cell.
You want to achieve this using the custom function.
Modification points:
Unfortunately, setValue cannot be used in the custom function. I think that the reason of your issue is due to this.
In your script, at cell.setValue(sheets[i].getSheetName);, the method of getSheetName is not run. If you want to use this method, please add () like getSheetName().
SpreadsheetApp.getActiveSpreadsheet() can be declared one time.
In order to achieve your goal, I would like to propose a custom function for creating an array including the sheet names and returning the array.
When above points are reflected to the sample script, it becomes as follows.
Modified script:
Please copy and paste the following script to the script editor of Google Spreadsheet. And, please put the custom function of =updateSheetTitles() to a cell. By this, the sheet names are returned to the row direction.
function updateSheetTitles() {
return SpreadsheetApp.getActiveSpreadsheet().getSheets().map(e => [e.getSheetName()]);
}
From your replying, when you want to retrieve the sheet names except for 1st and 2nd sheet, you can also use the following script.
function updateSheetTitles() {
return SpreadsheetApp.getActiveSpreadsheet().getSheets().map(e => [e.getSheetName()]).slice(2);
}
References:
Custom Functions in Google Sheets
map()
getSheetName()

Writing in a newly created Google spreadsheet using an external script

I have built a short script that takes inputs from a Google Form and creates a new spreadsheet. But when I try to set that values inside the sheet, nothing happens. Not sure if this is due to my code or to the lack of authorization given this is a newly created file. Here is my code:
var templates = DriveApp.getFilesByName('Template'); // get the files named Template (only one)
var template = templates.next(); // get the first files in the list
var newFile = template.makeCopy(name,newFolder); // Make a copy of the Template file and put it in NewFolder
var ss = SpreadsheetApp.open(newFile);
var sheet = ss.getSheets()[0];
sheet.getActiveRange('B1').setValue(name);
Thanks for your help
I think that in your script, an error occurs at getActiveRange('B1'). Because the method getActiveRange() of Class Sheet has not arguments. Ref I think that this is the reason of your issue. In this case, an error like The parameters (String) don't match the method signature for SpreadsheetApp.Sheet.getActiveRange. occurs. I thought that the reason of nothing happens might be that from your question, the script is run by the OnSubmit event trigger might be used. In this case, please modify as follows.
From:
sheet.getActiveRange('B1').setValue(name);
To:
sheet.getRange('B1').setValue(name);
When you modify like above and run again, the value of name is put to the cell "B2" on the 1st tab in the created Spreadsheet.
Note:
If you want to append the values when the script is run, please modify sheet.getActiveRange('B1').setValue(name); as follows.
sheet.appendRow([, name]); // In this case, the value is append to the column "B".
References:
getActiveRange()
getRange(a1Notation)

another IMPORTXML returning empty content

When I input
=IMPORTXML("http://www.ilgiornale.it/autore/franco-battaglia.html","//h2")
in my google sheet, I get: #N/A Imported content is empty.
However, when I input:
=IMPORTXML("http://www.ilgiornale.it/autore/franco-battaglia.html","*")
I get some content, so I can presume that access to the page is not blocked.
And the page contains several h2 tags without any doubt.
So what's the issue?
You want to know the reason of the following situation.
=IMPORTXML("http://www.ilgiornale.it/autore/franco-battaglia.html","//h2") returns #N/A Imported content is empty.
=IMPORTXML("http://www.ilgiornale.it/autore/franco-battaglia.html","*") returns the content.
If my understanding is correct, how about this answer?
Issue:
When I saw the HTML data of http://www.ilgiornale.it/autore/franco-battaglia.html, I noticed that the wrong point of it. It is as follows.
window.jQuery || document.write("<script src='/sites/all/modules/jquery_update/replace/jquery/jquery.min.js'>\x3C/script>")
In this case, the script tag is not closed like \x3C/script>. It seems that when IMPORTXML retrieves this line, the script tab is not closed. I could confirm that when \x3C is converted to <, =IMPORTXML("http://www.ilgiornale.it/autore/franco-battaglia.html","//h2") correctly returns the values of h2 tag.
By this, it seems that the issue that =IMPORTXML("http://www.ilgiornale.it/autore/franco-battaglia.html","//h2") returns #N/A Imported content is empty occurs.
About the reason that =IMPORTXML("http://www.ilgiornale.it/autore/franco-battaglia.html","*") returns the content, when I put this formula, I couldn't find the values of the script tab. From this situation, I thought that the script tag might have an issue. So I could find the above wrong point. I could confirm that when \x3C is converted to <, =IMPORTXML("http://www.ilgiornale.it/autore/franco-battaglia.html","*") returns the values including the values of the script tag.
Workarounds:
In order to avoid above issue, it is required to be modified \x3C to <. So how about the following workarounds? In these workarounds, I used Google Apps Script. Please think of these workarounds as just two of several workarounds.
Pattern 1:
In this pattern, at first, download the HTML data from the URL, and modify the wrong point. Then, the modified HTML data is created as a file, and the file is shared. And retrieve the URL of the file. Using this URL, the values are retrieved.
Sample script:
function myFunction() {
var url = "http://www.ilgiornale.it/autore/franco-battaglia.html";
var data = UrlFetchApp.fetch(url).getContentText().replace(/\\x3C/g, "<");
var file = DriveApp.createFile("htmlData.html", data, MimeType.HTML);
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
var endpoint = "https://drive.google.com/uc?id=" + file.getId() + "&export=download";
Logger.log(endpoint)
}
When you use this script, at first, please run the function of myFunction() and retrieve the endpoint. And as a test case, please put the endpoint to the cell "A1". And put =IMPORTXML(A1,"//h2") to the cell "A2". By this, the values can be retrieved.
Pattern 2:
In this pattern, the values of the tag h2 are directly retrieved by parsing HTML data and put them to the active Spreadsheet.
Sample script:
function myFunction() {
var url = "http://www.ilgiornale.it/autore/franco-battaglia.html";
var data = UrlFetchApp.fetch(url).getContentText().match(/<h2[\s\S]+?<\/h2>/g);
var xml = XmlService.parse("<temp>" + data.join("") + "</temp>");
var h2Values = xml.getRootElement().getChildren("h2").map(function(e) {return [e.getValue()]});
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange(sheet.getLastRow() + 1, 1, h2Values.length, 1).setValues(h2Values);
Logger.log(h2Values)
}
When you run the script, the values of the tag h2 are directly put to the active Spreadsheet.
References:
Class UrlFetchApp
Class XmlService
If I misunderstood your question and this was not the direction you want, I apologize.

Custom function throws a "You do not have the permission required to setValue" error

I am trying to set some value to a cell in a Google Spreadsheet:
function exampleFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range1 = sheet.getRange("A1");
var value1 = range1.getValue();
value1+=1;
range1.setValue(2);
return value1;
}
If I am trying to affect a cell with this function, this error appears:
You do not have the permission required to setValue. (line 10, file "ddd")
Do you know how I could make that possible? I actually want the affected cell to take the value of the cell A1 and increase the value of A1 by 1.
from the documentation :
Custom functions return values, but they cannot set values outside the cells they are in. In most circumstances, a custom function in cell A1 cannot modify cell A5. However, if a custom function returns a double array, the results overflow the cell containing the function and fill the cells below and to the right of the cell containing the custom function. You can test this with a custom function containing return [[1,2],[3,4]];.
reference : Custom Functions in Spreadsheets
It looks that you are using the above function as a custom function, in other words, it is called by cell formula on the Google Sheets UI, in the following way:
=exampleFunction()
Custom functions in Google Sheets have limitations like they can't be used to call Google Apps Script services that require permissions. The workaround is to use another mean to call the function:
Run it from the Google Apps Script Editor
Use a custom menu
Use a trigger
Also they could be called from dialogs and sidebars, Google Apps Script Web apps and by using the Google Apps Script execution API
It's just a little different than what we programmers think.
You can use setFormula in a Macro but not in a custom function.
Just create a simple macro from Tools > Macros > Record Macro, and then open the Script editor and change the Macro's code to your code...
Here is my Macro's code:
function SetFormula() {
var spreadsheet = SpreadsheetApp.getActive();
var formulaValue = spreadsheet.getRange('formulaText').getValue().toString();
spreadsheet.getRange('total').setFormula(formulaValue);
return formulaValue;
};
Then, to run your macro automatically (you can run that manually from Tools > Macros > YOUR-MACRO-NAME), just create a trigger as follows:
Open the Script Editor:
Then go to Triggers from the left side panel and tap on Add Trigger button:
Finally, create the trigger, select your Macro from the list (mine is SetFormula), select the Event Source as From SpreadSheet, the Event Type to On Edit, and save it.
That's it!
I named my ranges as FormulaText and total to be more flexible.
you can do that from here:
Custom functions do have permission limitations as noted above. They can run with a custom menu or you can insert an image and assign a custom script to it to use it like a button.
Using a Trigger is another way to accomplish something like this example, which makes it automatic.
A simple trigger in an App Script such as onSelectionChange(e) works without running into the permissions issue of putting a custom function into a cell. This trigger is newer than what was available in the original post. In the simple example below, cell A1 will turn white with an even integer and red with anything else. Granted, the speed at which the triggers fire may vary. It's not always as instantaneous as one might expect.
function onSelectionChange(e) {
const sheet = SpreadsheetApp.getActive()
var value1 = sheet.getRange("A1").getValue()
if(value1 % 2 == 0) {
sheet.getRange("A1").setBackground("#FFFFFF") //white
} else {
sheet.getRange("A1").setBackground("#FF0000") //red
}
}