using getData on a spreadsheet retaining font formatting - google-apps-script

I have the following script in my Code.hs file:
function getData() {
return SpreadsheetApp
.openById('17zMVbzevOV7HNpGIjkFKi_l_kUeuX7NX2jG5GIL8Jow')
.getActiveSheet()
.getDataRange()
.getValues();
}
However, if a cell contains:
"this is a test"
where "is" is in bold, when I get the data in the spreadsheet, I have:
"this is a test"
Without bolding.
Is there a way to get the text contents and the IN-string formatting (NOT the cell formatting) somehow?

Unfortunately there is not a way to get the in-string formatting. The closest I can find is a Range method getFontStyle()
However this returns 'italic' if the whole string is italic, and 'normal' if it isn't. It does not identify bold, nor does it identify if part of the string is bold or italic.

The Spreadsheet Service of Google Apps Script doesn't includes yet a method to handle the format for cell content parts. At this time one alternative is to use copyTo(destination) or copyTo(destination, options) from the Class Range.

Related

Canonical way to reference Elements

Is there a canonical way to set an ID—or other searchable, persistent data attribute—on Elements in Google Docs, such that I can easily refer back to it later? I'm looking for something equivalent to getElementById in javascript. Almost all examples I've seen, including Google's own docs, seem to reference objects by searching for text strings or inserting new strings.
I've found one reference in the NamedRanges class to a getId function, but I can't find any place to set that ID. I do see the setAttributes function on Elements but that seems to apply only for pre-defined attribute types. I haven't tested that, though.
In case it's relevant: my interest is in automatically creating a document from a Google Sheet and populating based on the current values in the sheet. I'd like to assign specific Elements individual IDs so I can easily retrieve the Element and replace the text if the values in the sheet change later on.
Turns out that this is possible using NamedRanges, I just didn't read carefully enough.
Note: All the following examples are working off this Google doc. You can make a copy and select "Script Editor" from the Tools menu to see the code.
You can assign named ranges pretty easily using Apps Script. The below code looks through the doc for [[TITLE]] and [[ABSTRACT]] and assigns named ranges to those chunks. Note that in the aforelinked doc I put them in a table to avoid issues with partial ranges.
function assignNamedRanges() {
const doc = DocumentApp.getActiveDocument();
const body = doc.getBody();
const placeholders = ["title", "abstract"];
placeholders.forEach(p => {
const rangeBuilder = doc.newRange();
const text = body.findText("[[" + p.toUpperCase() + "]]");
rangeBuilder.addElement(text.getElement());
doc.addNamedRange(p, rangeBuilder.build());
});
}
Once you assigned them, you can update the range to something else in a separate function:
function updateNamedRanges() {
const doc = DocumentApp.getActiveDocument();
const body = doc.getBody();
const title = doc.getNamedRanges("title")[0];
const abstract = doc.getNamedRanges("abstract")[0];
title.getRange().getRangeElements()[0].getElement().asText().setText("Bob");
abstract.getRange().getRangeElements()[0].getElement().asText().setText("I like pancakes");
}
Note that NamedRanges are persistent, and the multiple NamedRange instances can have the same name. This means that if you run the first function four times, you'll have eight named ranges. You can make a convenience function to clear all those out pretty easily:
function clearNamedRanges() {
DocumentApp.getActiveDocument().getNamedRanges().forEach(r => {
r.remove();
})
}
So I been checking the documentation about elements for Google Docs in AppScript and it seems that some of them can be modified but not as freely as it looks as noted in the documentation:
Elements shown in bold can be inserted; non-bold elements can only be manipulated in place.
I tried checking with setAttributes as you mentioned however the attributes itself can only be from a document elements like: TEXT, PARAGRAPH, TABLE, ETC, this elements can't receive an ID as there is not method to insert an specific ID as you are requiring, most of the values that can be inserted are specific element attributes like: Font size, Font family, etc.

How can I have the text I enter into each cell automatically appended to the same root URL without creating any new columns?

I am rephrasing my original question to make it more intelligible.
I put spaces between "https" below because StackOverflow would not allow me to enter the actual URL.
Of course I could enter the text "apple" in cell A1 and then add a link from A1 to foo.com/Apple so that I would end up with [Apple](h t t p s://foo.com/Apple) in A1. However, I want that done automatically.
In other words, I want to...
Go to a cell, and then
type "apple" on my keyboard, and then
press enter, and then
instead of merely seeing "apple" in plain text, I actually I want to see
"apple" hyperlinked to foo.com/Apple like this [Apple](h t t p s://foo.com/Apple).
To get what you want you can use Google Apps Script. Adding a trigger that fires every time column A is edited, getting the entered value and combining it with the =HYPERLINK(url, link_label) function, should get what you need.
Open the Google Apps Script editor, Tools>Script Editor
Add the following code
function onEdit(e) {
// Check the edit is in column A
if(e.range.getColumn()==1){
// Get the values from the onEdit
let cell = e.range.getA1Notation()
let value = e.value
let ss = e.source
// Adding as HYPERLINK Formulas
ss.getRange(cell)
.setFormula(`=HYPERLINK("https://example.com/${value}","${value}")`)
}
}
Documentation
HYPERLINK Function
onEdit(e) Trigger
SpreadSheetApp Overview
try:
=INDEX("foo.com/"&B2:B3)

How to apply the same conditional formatting rule to multiple columns in Google sheets using apps script?

I have multiple columns - G, L, Q, V, AA, AF - to which I want to apply a conditional format rule in Google sheets. Starting in row 2 - so that I don't include the header row - I want any given cell in the specified column to .setBackgroundColor ("orange") if any text or data is found to the right of that cell. For example, I want cell G2 to be orange if cell H2 has anything entered inside of it, and L17 to be orange if S17 has data, and AA5 to be orange if AD5 is not blank.
My experience with apps script is very primative. I can only successfully write very few lines of code, so my dilemma is past my ability. I understand it is possible to apply conditional formatting using sheets' built in conditional formatting tab, but it will not work for my project seeing as I am gathering data with a Google form, and with every response I receive from the form, the sheet creates a new line for the submission that retains none of the formatting from the rest of the sheet. My plan would be to add a form submission trigger to the code so that the conditional formatting in the columns updates regularly with the sheet.
I have been looking around for some time online and have not found a solution to my problem. Any help would be greatly appreciated! Thank you!
The following example shows the process of creating a new conditional format rule, applying it to the sheet. Then copying and pasting the format of a range to a target range. You can find the references for the methods used in the references section at the end of this answer.
Example:
function myFunction() {
// The range where the formatting will be applied.
var range = 'G2:G'
// Get the array containing the conditional formatting rules for the sheet
var spreadsheet = SpreadsheetApp.getActive();
var conditionalFormatRules = spreadsheet.getActiveSheet().getConditionalFormatRules();
// Build the new conditional formatting rule and push it to the array
conditionalFormatRules.push(SpreadsheetApp.newConditionalFormatRule()
.setRanges([spreadsheet.getRange(range)]) // The range to apply to
.whenFormulaSatisfied('=H2<>""') // The formula to check
.setBackground('orange') // The format to apply
.build());
// Set the conditional format rules for the sheet with the updated array
spreadsheet.getActiveSheet().setConditionalFormatRules(conditionalFormatRules);
// Paste the format of the range into the next desired range
spreadsheet.getRange(range).copyTo(spreadsheet.getRange('L2:L'), SpreadsheetApp.CopyPasteType.PASTE_FORMAT, false);
/* repeat the previous instructions for all your target ranges replacing "L2:L" with the target range */
};
References:
-getConditionalFormatRules()
-newConditionalFormatRule()
-setConditionalFormatRules()
-copyTo(destination, copyPasteType, transposed)

Google Sheets Script – Function that outputs the background color of a cell

I would like to write my own function in Google Script that I can use in Google Sheets to get the background color of a cell. Let's call the function GETBACKGROUNDCOLOR.
I want to be able to pass a cell reference (in A1 notation) as parameter, e.g. if I call =GETBACKGROUNDCOLOR(B2), it should return the background color of cell B2. If I call the function without parameter, I want it to return the background color of the same cell in which it is called, e.g. calling =GETBACKGROUNDCOLOR() in C3 should return the background color of C3.
I have tried this:
function GETBACKGROUNDCOLOR(cell){
return SpreadsheetApp.getActiveSheet().getRange(cell).getBackground();
}
But when I call GETBACKGROUNDCOLOR(A1), the parameter is not the cell reference of A1, but the cell content of A1.
How can I solve this?
Bulletproof script below:
1. Paste to AppsScript:
function bgHex(cellAddress) {
var mycell = SpreadsheetApp.getActiveSheet().getRange(cellAddress);
var bghex = mycell.getBackground();
return bghex;
}
2. Back in sheets call it with =bgHex(CELL("address"; B4)) or =bgHex(CELL("address", B4)), depending on your locale. Look closely and note the difference: delimiters. Some countries like Poland use comma as decimal delimiter and we use semicolons for separating arguments. Others use dots as delimieters so they can use comma for separating args.
With that function you'll get the hex code for specified cell's background. Call any cell, no range errors, no mismatched data formats.
Now you can play with getting other stuff out of these cells, for example check cell values. Here's a test sheet so You can see how it works.
You will find a second function there, for extracting cell's value – the principle is the same.
function cellValue(cellAddress) {
var mycell = SpreadsheetApp.getActiveSheet().getRange(cellAddress);
var value = mycell.getValue();
return value;
}
Now go to https://developers.google.com/apps-script/reference/spreadsheet/range and play with other methods.
You could try something like
=GETBACKGROUNDCOLOR("A"&row(A1))
or
=GETBACKGROUNDCOLOR(cell("address", A1))
and see if that works?
(Note: depening on your locale you may have to use a semi-colon instead of a comma).
You can use =address to resolve name
=GETBACKGROUNDCOLOR(address(row(A1),column(A1)))
for relative cells or
=GETBACKGROUNDCOLOR(address(row(),column()))
for cell with formula
However, the big problem would be that then you'll change cell backround color, formula won't recalculate(function re-eval won't trigger), so it might not be exactly a way.
Example sheet:
https://docs.google.com/spreadsheets/d/1lfFRLVqhns0AJCbZd6ikgcDtvktcgpNWfNomMASWemE/edit#gid=0

Bold conditional formatting script for Google Spreadsheets

What I want to do is essentially what this user wanted to do here:
I need a script that formats the cells in column A bold, but only the cells that contain the word 'Hello'.
However I have no knowledge of Google Apps scripts at all, and I need an answer put in much simpler terms than what I could find there or anywhere else. Any help is appreciated; thank you!
To start, from your spreadsheet, open "Tools / Script Editor...". When the dialog opens, choose to "Create Script For... Spreadsheet". You will end up with a sample script - we're going to edit it to do what you want.
Change the readRows() function as shown here. The change is that instead of logging the content of every row, we will use an if statement to check if the cell contains a string with 'Hello' in it. Then, if it does, we'll bold the cell text.
function readRows() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
// Arrays start at 0, Google Sheets start at 1 - must remember that.
// We will loop starting at 1, because we want to skip the header in
// Row 1, aka Array index 0
for (var i = 1; i <= numRows - 1; i++) {
var colA = values[i][0];
if (colA.toString().indexOf('Hello') >= 0) {
sheet.getRange(i+1,1).setFontWeight("bold");
}
}
};
Now, how to run that? The sample already has an onOpen() function that will set up a custom menu... let's just change the text it displays in the User Interface, as shown here. The only change is in the 'name' property of the menu entries.
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Bold Hello",
functionName : "readRows"
}];
sheet.addMenu("Script Center Menu", entries);
};
Save your script. Go back to your spreadsheet, and reload it (to get it to run the onOpen trigger function). When your menu shows up, you're all set.
Next - start with the "First Script" tutorial here. The Google Apps Script documentation covers all the services provided by Apps Script, but the basic language structure and objects are javascript, so you should get familiar with that. Just try googling "learn javascript", and you'll find tons of tutorials, books, and other resources.
I can't make this simpler.
In the now not so new 'New' Sheets this can be achieved without a script:
Clear formatting, select ColumnA and Format, Conditional formatting..., Format cells if... Text contains and:
hello
Then for Formatting style click the B and Done.
This way is not case sensitive and will embolden contents such as OTHELLO.
If you aren't trying to set too many conditional formatting rules, there's an easier way to set colors, though not bold. In Google Drive Spreadsheet, click the "Format" menu. The bottom menu item should be "Conditional formatting..."; click that. That should produce a dialog box that defaults to something like this (to the extent that I can draw it with text):
x
Conditional formatting
[Text contains ◊ ] [ ] []Text: [ ] []Background: [ ] x
e.g. "done" or "Jonathan"
_______________________________________________________________________________
+ Add another rule
[ Save rules ] [ Cancel ]
In your example, you're looking for cells that contain "Hello", so the default of "Text contains" would do the job. Put "Hello" into the text box, and set a format in the "Text":" and "Background:" boxes. That doesn't give you bold, but it does allow colors.
I see that your question dates back half a year, so it's probably too late for you (and if you strictly need bold, it doesn't solve the problem anyway), but it may help others.