Add button on the fly - google-apps-script

I read the tutorial regarding creating buttons, it was pretty simple & straightforward.
However, I want to create buttons on the fly. I have a status column. It has a drop down menu created from a range of cells on a hidden sheet. This is working code.
Here's the wish list. When “status X” is selected, I want the button to appear. The user can then push the button to increment a counter (which is displayed). Track needs to be kept of the date & time (also displayed – including the history) when the button is pressed. The previous button press date & time is always displayed.
Once the counter reaches some number – say 10, a warning message pops up which the user must acknowledge, and then the status automagically changes, button disappears and the entire row (with a color change) is moved to another page (or possibly another spreadsheet entirely).
Currently I have a function that simply changes the row color when the status changes, and also moves the row to another page. I imagine the button would get created around that point somehow. Other than what the tutorial says which I don't think really applies, I don't really know how to move forward. Any help?
Please note for the record I find using the “active page” a bad idea in my case, as I have several pages where this might execute from. So, I use “sourceSheet” and “targetSheet” for everything.
Thank you
//happyFunTime is installed as an edit trigger.
function happyFunTime() {
var s = SpreadsheetApp.getActiveSheet();
var r = s.getActiveCell();
// set row color as soon as we know the status
var i = r.getRow();
var targetSheetName=setRowColor(i);
// moveRow(targetSheetName);
}
function setRowColor(i)
{
var sName;
var outCome = [];
var color;
var range = SpreadsheetApp.getActiveSheet().getDataRange();
var statusColumnOffset = getStatusColumnOffset();
var rowRange = range.offset(i-1, 0, 1);
var status = rowRange.offset(0, statusColumnOffset).getValue();
outCome=checkStatus(status);
sName=outCome[0];
color=outCome[1];
rowRange.setBackgroundColor(color);
return sName;
}

Have you tried writing any code to achieve this programmatically? If you do, post it here, as its easier to gain a better understanding of your working code, and to build on it. Or do you just want someone to write te code for you?
All you have described is certainly possible to achieve programmatically, I suggest researching the onEdit trigger, as a means to an end.

Related

How to filter data by cell color AND if checkbox value is true in Google Sheets

I have a spreadsheet of games that my friends and I play.
We are trying to find a reliable way of figuring out which games we all want to play.
We have created a Google Sheet that lists expected information such as Game Name, a link to the game, number of max players, price, description, game type and finally but most importantly, the traffic light system as to whether or not we want to play it.
Green cell color means we want to play it.
Yellow cell color means we wouldn't mind giving it a try, or only in certain circumstances (i.e only for a short amount of time).
Red cell color means we don't want to play it.
These cell colors are listed against each game, vertically down by each person's name.
O in the colored cell indicates that this person already owns that game.
I want to create a filter view (or somehow a saved, accessible filter) that allows me to click on the checkbox of particular people, and then the list is filters by games that are GREEN.
This would mean that I could click the checkbox for Alex and Rach, and then the list would be filtered only by games that Alex and Rach want to BOTH play (as their cell color is green).
How do I go about achieving this?
Many thanks in advance
We don't normally code for you but here's a what you can do using Google Apps Script.
Try:
Using Google App Script, use the following code:
function onEdit(e) {
var ss = e.source.getActiveSheet();
var lastRow = ss.getLastRow();
var range = e.range;
var col = range.getColumn();
var colColors = ss.getRange(4, col, lastRow - 3).getBackgrounds();
colColors.forEach((color, index) => {
var row = ss.getRange(index + 4, col)
if (col >= 8 && col <= 12 && e.range.getValue() == true) {
color == "#00ff00" ? null : ss.hideRow(row);
} else {
ss.getRange("H2:L2").uncheck();
ss.showRows(row.getRow());
}
})
};
The code uses onEdit Trigger which runs the script everytime you make edits. This then checks if the column you make edits on are on the Columns H-L and then gets the colors of that column which checkbox you checked and filters only those with Green background color.
Result:
The only limitation is it does not reverse the filter so if you uncheck a checkbox it resets all.
Take note also of the color, it is on a hex value. In my test sample the green is equivalent to #00ff00. You might need to change this according to the hex value equivalent to the color green you are using.
Let me know if this works for you.

How to copy and paste specific information in a same doc using Google App Script?

I'm very new using GAS what I'm trying to do is to copy some information that is already on the bottom of my doc to anywhere I want in my docs, this should work just by copying the information and paste it at the place I desire, but I want it to be done with Google App Script because it's a daily task and it's easier to do it with a function, instead of copying and pasting manually. Searching on how to do this, I found a lot of information about how to do it on Spreadsheets, but I needed it to be done on Google Docs. How can I do that?
If someone can guide me or send me a link to another similar question that would be very helpful, I don't know where to start.
This is what I have until now, I get all the data of the current doc and set it again to the page, the code gives me problems because it deletes my other information, also it selects all the doc's information. I want to select a piece of specific information and don't copy the content style.
function copyPasteInfo() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var notesText = body.getText();
body.appendPageBreak();
body.setText(notesText);
}
Link to the doc document
https://docs.google.com/document/d/1s2TCspXbjvHVurwhIWSdwJ_hMcZIoLTKj4FAB82nmhM/edit
Video example of how what i want to do
https://www.screencast.com/t/UmEon8Fm0lPe
Picture of the information i'm trying to copy and paste to the bottom of my doc
If I correctly understood your question, this code will help you to achieve your goal.
let body = DocumentApp.getActiveDocument().getBody();
function moveTable() {
// Get the last table and the previous table found in your Doc
const [previousTable, bottomTable] = getDesireTables();
// Make a copy of your last table
const bottomTableCopy = bottomTable.copy();
// Get the previous table's index
const previousTableIndex = body.getChildIndex(previousTable);
// Insert the last table's copy under the previous table in your Doc
body.insertTable(previousTableIndex + 1, bottomTableCopy);
// Remove the original last table
body.removeChild(bottomTable);
}
function getDesireTables(){
const tablesArr = body.getTables().slice(-3);
// Get the parent element type to check if it's a cell
const parentELementType = tablesArr[tablesArr.length - 1].getParent().getType();
if(parentELementType === DocumentApp.ElementType.TABLE_CELL){
// If there's a table inside a table, return this
return tablesArr.slice(0, 2);
}
else{
return tablesArr.slice(-2);
}
}
What I did was to get the last two tables in the Doc, then I made a copy of the last one and with the index of the previous one, I inserted it under the previous one.
Edit
I noticed you had a table inside a table. therefore I added the getDesireTables function. Which it will check if your bottom table has a table inside.
Docs
These are the docs I used to help you:
getTables().
copy().
insertTable(childIndex, table).

Prompt box, ask value, store in cell. Add button for adapt

I'm new to the world of google sheets. I was able to program very simple routines in Excel. Sorry if I couldn't find the answer anywhere on this site.
My question is simple, has 2 parts:
I would like to prompt a question box on opening the google sheet, asking a numeric value, after answering this, storing the numeric value to a specific cell, e.g. B3.
I would like to have a button on multiple pages of the sheet, prompting the same question box to edit the value.
Background:
I'm a medical doctor, I've created a sheet with many medications for small children in emergency situations. I would like a ask the user the age (and maybe later the weight, it's now calculated) of the child, so the age and weight are correct on all pages and the user is not in a hurry to find the right cell. If the user would like to edit the age, he uses the button, available on all sheets.
First, with the following function you can ask the user for the age and set it to a specific cell in a specific sheet:
function promptUserAge()
{
// Prompt for the value
var age = SpreadsheetApp.getUi().prompt("Please enter the age.").getResponseText();
// Get the sheet that you want store the value in and set the value in the cell B3
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Patient Information").getRange("B3").setValue( age );
}
Now, since you used the phrase
so the age and weight are correct on all pages
I am not sure if you are going to set the value in multiple sheets, and are you going to do that with Sheets functions or app script. That is why I am going to include the method to set the value in all sheets, in specific cell:
function promptUserAge()
{
// Prompt for the value
var age = SpreadsheetApp.getUi().prompt("Please enter the age.").getResponseText();
//Get all pages in spreadsheet and iterate through
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for(var i = 0; i < sheets.length; i++ )
{
//Set the value in the cell B3 of the page
sheets[i].getRange("B3").setValue( age );
}
}
To get the prompt to pop-up when user opens the Spreadsheet you use call it in the onOpen function:
function onOpen(e)
{
promptUserAge();
}
And now, the part where you add the button to edit the value.
You could add a custom menu that would appear in the Spreadsheet's toolbar with a custom menu like this :
function createMenu()
{
SpreadsheetApp.getUi().createMenu("Fill information")
.addItem("Age", "promptUserAge")
.addToUi();
}
This is a good way if your users know what to look for, meaning that you have told them the custom menu exists. From my experience, some people have trouble finding the menu even if you tell them it is there, so since your background suggests you want to create an easy and fast way I would use the second method:
You could insert an image to the Spreadsheet by navigating from it's toolbar: Insert -> Image. Insert an Image that says 'Push here to edit information' in a very clear way and right click the image. You get the borders to edit the image size and in it's upper-right corner appears three dots. You click the dots and then "Assign script...". To the prompt, insert the function name without the parenthesis. In this example you would insert:
promptUserAge
Now the image works as a button to call the function.
I hope this helped.

Increment Cell value by one by clicking

I want to write a formula in Google sheets so that I can attach to a button to make the value in a cell increase by 1 each time you click it.
Here is the best I could find on this topic.
I attempted to do this, but to no avail. I am using Windows 7 and Chrome.
First create a script to increment the cell value. For instance, to increment cell "A1" by one the following script would work:
function increment() {
SpreadsheetApp.getActiveSheet().getRange('A1').setValue(SpreadsheetApp.getActiveSheet().getRange('A1').getValue() + 1);
}
Save this script and open your spreadsheet and follow these steps:
Go to "Insert" > "Drawing" and draw a button using shapes and text.
Position your button accordingly, and right-click on it to display an
arrow on the side of it.
In the drop-down menu, select "Assign Script." and type the name of
your function. (In this case "increment")
The first time when you click on it, it'll ask for permission but
should work subsequently.
Update 2020
Motivation:
I would like to provide a different approach based on Marlon's comment:
what if we want to create multiple buttons to different rows and each
button modifies its own row? Is there a way not create this manually?
Solution:
You can use onSelectionChange to capture single click events and mimic the behaviour of a button.
The following script will increment the value of a cell in column A upon clicking on a cell in column B of the same row.
It also creates a "button" text on the fly but this is optional and up to the user.
To use this solution, simply copy & paste it to the script editor and save the changes. After that, it will automatically be used upon single click events. In this solution I used Sheet1 as the sheet name.
function onSelectionChange(e) {
const as = e.source.getActiveSheet();
const col = e.range.getColumn();
const row = e.range.getRow();
if (as.getName() == 'Sheet1' && col == 2){
const range = as.getRange(row,1);
range.setValue(range.getValue()+1);
e.range.setValue('Button');
}
}
Demonstration:
Restrictions:
While this approach works pretty well, there are two drawbacks:
There is a small delay between the clicks and the updates.
The trigger is activated when selecting a cell. If a cell is already selected, if you click it again the function won't trigger. You have to remove the current selection and then select it again.
Assign the following to a drawing inside your sheet and it will increase the value of the currently selected cell by 1:
function plusOne() {
var cell = SpreadsheetApp.getActiveSheet().getActiveCell();
var value = cell.getValue() * 1;
cell.setValue(value+1);
}

Get page selection including HTML?

I'm writing a Chrome Extension, and I was wondering if it was possible to get the selected text of a particular tab, including the underlying HTML? So if I select a link, it should also return the <a> tag.
I tried looking at the context menu event objects (yes, I'm using a context menu for this), and this is all that comes with the callback:
editable : false
menuItemId : 1
pageUrl : <the URL>
selectionText : <the selected text in plaintext formatting, not HTML>
It also returns a Tab object, but nothing in there was very useful, either.
So I'm kind of at a loss here. Is this even possible? If so, any ideas you might have would be great. Thanks! :)
Getting the selected text of a page is fairly easy, you can do something like
var text = window.getSelection().toString();
and you'll get a text representation of the currently selected text that you can pass from a content script to a background page or a popup.
Getting HTML content is a lot more difficult, mostly because the selection isn't always at a clean HTML boundary in the document (what if you only select a small part of a long link, or a few cells of a table for example). The most direct way to get all of the html associated with a selection is to reference commonAncestorContainer, which is a property on a selection range that corresponds with the deepest node which contains both the start and end of the selection. To get this, you'd do something like:
var selection = window.getSelection();
// Only works with a single range - add extra logic to
// iterate over more ranges if needed
var range = selection.getRangeAt(0);
var container = range.commonAncestorContainer;
var html = container.innerHTML
Of course, this will likely contain a lot of HTML that wasn't actually selected. It's possible that you could iterate through the children of the common ancestor and prune out anything that wasn't in the selection, but that's going to be a bit more involved and may not be necessary depending on what you're trying to do.
To show how to wrap this all up into an extension, I've written a short sample which you can reference:
http://github.com/kurrik/chrome-extensions/tree/master/contentscript-selection/
If you don't want all of the siblings, just the selected HTML, use range's other methods like .cloneContents() (to copy) or .extractContents() (to cut).
Here I use .cloneContents():
function getSelectedHTML() {
var range = window.getSelection().getRangeAt(0); // Get the selected range
var div = document.createElement("div");
div.appendChild(range.cloneContents()); // Get the document fragment from selected range
return div.innerHTML; // Return the actual HTML
}