I'm trying replace first letter of the text in a cell to upper case when a cell edited. For that I'm using onEdit script trigger:
function onEdit(e)
{
e.range.setValue(e.range.getValue().replace(/^./, a => a.toUpperCase()));
}
It works well, until I try undo in the spreadsheet, than onEdit fires again and replaces the text again.
So is there a better way to achieve this?
First Letter to UpperCase
function onEdit(e) {
//e.source.toast('Entry')
const sh = e.range.getSheet();
if(sh.getName() == 'Sheet0' && e.range.columnStart == 1 && e.range.rowStart > 1 && e.value) {
//e.source.toast('Flag1')
e.range.setValue(e.value.replace(/^\s*(\w)/, a => a.toUpperCase()));
}
}
Apparently the event object that onEdit receives contains value and oldValue properties, they both set to undefined when used undo.
So this seems to be working much better (it also ignores white space infront):
function onEdit(e)
{
if (e.value)
{
const val = e.value.replace(/^\s*./, a => a.toUpperCase());
if (val !== e.value)
e.range.setValue(val);
}
}
It still not a perfect solution, because it has delay in the execution and also it might skip cells if multiple edits in multiple cells were done in short period of time.
Related
What I want to do:
I write a row number in a cell, let's say F1, and the value is 9.
I want to add a button that calls a script that will delete the line as I wrote it into the cell, so in this example, line 9.
So I start with:
var s = SpreadsheetApp.getActive()
var sheet = s.getSheetByName('Books');
var row = sheet.getRange('F1');
However this:
sheet.deleteRow(row.getValue());
will give an out of bounds error. getDisplayValue does not work as well and putting it into an parseint doesn't work either.
The 9 hardcoded
sheet.deleteRow(9);
works of course.
I also debbuged and did a
sheet.getRange('G1').setValue(row.getValue());
to verify that the 9 is indeed there, which it is, the 9 will be copied to cell G1.
sheet.getRange('A'+row.getValue()+':J'+row.getValue()).clearContent();
meanwhile will have it treat it as a A:J and delete every line not just one. Doing toString after getValue did also not change anything.
What am I missing here to turn the cell value into a range?
Sheet.deleteRow(value of F1):
function onEdit(e) {
const sh = e.range.getSheet();
if(sh.getName() == "Your sheet name" && e.range.columnStart == 6 && e.range.rowStart == 1 && e.value) {
sh.deleteRow(e.value)
}
}
Cannot call this function from script editor must save in project and edit F1 of Your Sheet. You also need to edit line 3 and add the name of the appropriate sheet.
Using a checkbox for a button:
function onEdit(e) {
e.source.toast("Entry")
const sh = e.range.getSheet();
if(sh.getName() == "Sheet0" && e.range.columnStart == 1 && e.range.rowStart == 1 && e.value == "TRUE") {
e.source.toast("Gate1")
e.range.setValue("FALSE")
sh.deleteRow(sh.getRange("F1").getValue());
}
}
Demo:
Good morning:
This time I have a spreadsheet in Google Sheets, in which in column "A", users will be entering their identity document. Ideally, numerical or alphanumeric values should be entered in the case of foreign documents. But, many times these users paste the data from other forms, so they may come with special characters ("." ;","; "-"; "_"). The idea is that when the user enters a data, a macro is run, which removes the format of that entire column, cleans it and eliminates those special characters, only leaving numbers and letters to have.
The easy step is to do it with a formula in a contiguous column, the formula is REGEXREPLACE(), but already the document becomes heavier and such. That's why I wanted to use the macro option, since it would be acting on the same value of the cell and reducing it in many cases.
I still have no knowledge of Appscript and have been searching on Youtube, but I did not find anything.
Could you help me create this macro?
I thank you very much for being so!!!
Miguel.-
Remove unwanted whitespace:
function onEdit(e) {
//e.source.toast("Entry");
const sh = e.range.getSheet();
const names = ["Sheet1","Sheet2"];//You can changes these sheet names and add as many as you wish
const idx = names.indexOf(sh.getName());
if(~idx && e.range.columnStart == 1 && e.value) {
//e.source.toast('Gate1')
e.range.setValue(e.value.replace(/[-_,:;\.]+/g,''));
}
}
Regex
Learn More
Bitwise Not(~)
try this:
function onEdit(e) {
//e.source.toast("Entry");
const sh = e.range.getSheet();
const names = ["Sheet1","Sheet2"];//You can changes these sheet names and add as many as you wish
const idx = names.indexOf(sh.getName());
if(~idx && e.range.columnStart == 1 && e.value) {
//e.source.toast('Gate1')
sh.getRange("A5:A" + sh.getLastRow()).getValues().flat().forEach((el,i) => {
sh.getRange(i + 5,1).setValue(el.replace(/[-_,:;\.]+/g,''));
})
}
}
Here is an example of an onEdit(e) function that will remove unwanted characters. The String.replace() is using a regex expression with the special character dot escaped \.. The same would need to be done for any other special characters.
The onEdit is limited to column A of Sheet1.
function onEdit(e) {
try {
if( e.range.getSheet().getName() !== "Sheet1" ) return;
if( e.range.getColumn() !== 1 ) return;
let value = e.value;
if( !value ) { // copy/paste
value = e.range.getValue();
}
value = value.replace(/[-_,;\.]/g,'');
e.range.setValue(value);
}
catch(err) {
SpreadsheetApp.getActiveSpreadsheet().toast("onEdit() "+err);
}
}
Reference
onEdit(e) trigger
String.replace()
Regexr
I'm looking for a way to use a dropdown(B5) trigger, just any edit, to copy the value of a cell(L6) to another(E3). I can't put the formula directly in the cell as I need to be able to edit it.
I've really hit a wall, the help would be appreciated!
Try using triggers in your script, this can be done by having specific function names, like onEdit(e).
Sample Code:
function onEdit(e) {
const sheet = e.range.getSheet();
if (e.range.getA1Notation() == "B5") {
sheet.getRange("E3").setValue(sheet.getRange("L6").getValue());
}
}
Note:
When you put this code in the script editor, if you run this manually (perhaps to authenticate), it will return an TypeError error. The script will run properly every time cell B5 is edited.
Sample Output:
function onEdit(e) {
const sh = e.range.getSheet();
if (sh.getName() == 'Enter Sheet Name' && e.range.columnStart == 2 && e.range.rowStart == 5) {
sh.getRange(3,5).setValue(sh.getRange(5,2));
}
}
Here is the desired behavior:
Cell A1 contains a number (0 by default) representing damage taken.
Cells B1:D1 contain checkboxes (FALSE by default).
When cell B1 is edited (i.e. checked "TRUE"), cell A1 is decreased by 1. Cell B1 is automatically reset to FALSE. If cell A1 is decreased below 0, it is reset to 0).
When cell C1 is edited, cell A1 is incremented by 1 and automatically reset to FALSE.
When cell D1 is edited, cell A1 is reset to 0.
I'm very new to Google Apps Script and recently discovered Simple Triggers. It seems like onEdit is meant to trigger when anything in the entire workbook is edited, but I'm not sure. Is it possible for a script to run automatically if a specific range is edited (i.e. B1:D1 as described above)?
For context, I'm designing a character sheet for a play-by-post RPG. When a character takes damage, their capability in one or more skills is decreased by taking damage ranks. To make it as simple as possible to track these throughout the sheet, I'm trying to find a way to easily increment these ranks up and down without having to write a separate macro for each row.
If there's a better solution than the behavior I've proposed (e.g. using something other than checkboxes and onEdit triggers), please let me know! Also, it would be ideal if the solution also worked on mobile.
/*
Cell A1 contains a number (0 by default) representing damage taken.
Cells B1:D1 contain checkboxes (FALSE by default).
When cell B1 is edited (i.e. checked "TRUE"), cell A1 is decreased by 1. Cell B1 is automatically reset to FALSE. If cell A1 is decreased below 0, it is reset to 0).
When cell C1 is edited, cell A1 is incremented by 1 and automatically reset to FALSE.
When cell D1 is edited, cell A1 is reset to 0.
*/
function onEdit(e) {
var sh=e.range.getSheet();
e.source.toast('Entry');
if(sh.getName()!="Sheet1")return;//might need to change Sheet1 to whatever you're using
if(e.range.columnStart==2 && e.range.rowStart==1 && e.value=="TRUE") {
e.source.toast("B1");
var vA1=e.range.offset(0,-1).getValue();
e.range.offset(0,-1).setValue((vA1-1<0)?0:vA1-1);
e.range.setValue("FALSE");
}
if(e.range.columnStart==3 && e.range.rowStart==1 && e.value=="TRUE") {
e.source.toast("C1");
var vA1=e.range.offset(0,-2).getValue();
e.range.offset(0,-2).setValue(vA1+1);
e.range.setValue("FALSE");
}
if(e.range.columnStart===4 && e.range.rowStart==1 && e.value=="TRUE") {
e.source.toast("D1");
e.range.offset(0,-3).setValue(0);
e.range.setValue("FALSE");
}
}
Just a reminder: You can't run this function from the script editor just install it in a .gs file and play with your check boxes on Sheet1. If you not using Sheet1 the change the name of the sheet in the script to whatever you sheet name is.
It looks like this will work for "A1:D".
function onEdit(e) {
var sh=e.range.getSheet();
e.source.toast('Entry');
if(sh.getName()!="Sheet1")return;
if(e.range.columnStart==2 && e.value=="TRUE") {
e.source.toast("B1");
var vA1=e.range.offset(0,-1).getValue();
e.range.offset(0,-1).setValue((vA1-1<0)?0:vA1-1);
e.range.setValue("FALSE");
}
if(e.range.columnStart==3 && e.value=="TRUE") {
e.source.toast("C1");
var vA1=e.range.offset(0,-2).getValue();
e.range.offset(0,-2).setValue(vA1+1);
e.range.setValue("FALSE");
}
if(e.range.columnStart===4 && e.value=="TRUE") {
e.source.toast("D1");
e.range.offset(0,-3).setValue(0);
e.range.setValue("FALSE");
}
}
Modified a bit to help you test it
function onEdit(e) {
var sh=e.range.getSheet();
var cell=e.range.getA1Notation();//debug
e.source.toast('Entry');//debug
if(sh.getName()!="Sheet1")return;
if(e.range.columnStart==2 && e.range.rowStart<15 && e.value=="TRUE") {
e.source.toast(cell);//debug
var vA1=e.range.offset(0,-1).getValue();
e.range.offset(0,-1).setValue((vA1-1<0)?0:vA1-1);
e.range.setValue("FALSE");
}
if(e.range.columnStart==3 && e.range.rowStart<15 && e.value=="TRUE") {
e.source.toast(cell);//debug
var vA1=e.range.offset(0,-2).getValue();
e.range.offset(0,-2).setValue(vA1+1);
e.range.setValue("FALSE");
}
if(e.range.columnStart===4 && e.range.rowStart<15 && e.value=="TRUE") {
e.source.toast(cell);//debug
e.range.offset(0,-3).setValue(0);
e.range.setValue("FALSE");
}
}
Please note: there should only be one onEdit(e) simple trigger function. If fact there should only be one of any function in a .gs file.
You may wish to remove my e.source.toast(); lines
In This Example this trigger if for MainSheet, and cells of b, c and d are boolean type, not string:
function MainSheetOnEdit()
{
var spreadsheet = SpreadsheetApp.getActive();
var mysheet=spreadsheet.getActiveSheet();
if (mysheet.getSheetName()!='MainSheet') return;
if(mysheet.getCurrentCell().getColumn()==2)
{
if (mysheet.getCurrentCell().getValue()==true){
mysheet.getCurrentCell().setValue(false);
if(mysheet.getCurrentCell().offset(0, -1).getValue()>0)
{
mysheet.getCurrentCell().offset(0, -1).setValue(mysheet.getCurrentCell().offset(0, -1).getValue()-1);
}
}
}
if(mysheet.getCurrentCell().getColumn()==3)
{
if (mysheet.getCurrentCell().getValue()==true){
mysheet.getCurrentCell().setValue(false);
mysheet.getCurrentCell().offset(0, -2).setValue(mysheet.getCurrentCell().offset(0, -2).getValue()+1);
}
}
if(mysheet.getCurrentCell().getColumn()==4)
{
if (mysheet.getCurrentCell().getValue()==true){
mysheet.getCurrentCell().setValue(false);
mysheet.getCurrentCell().offset(0, -3).setValue(0);
}
}
}
I am trying to create a script on Google Sheets to receive a message alert whenever the value of the B10 cell increases. Something like this:
Static oldVal As Variant
oldVal = Me.Range("B10").Value
If Me.Range("B10").Value > oldVal Then
Browser.msgBox('hello world');
End If
There is a way?
Other than your syntax being way off (Apps Script is based on JavaScript), yes, you can do that using a couple different mechanisms.
Use the onEdit(e) simple trigger to watch the sheet.
The e object has an oldValue and a value key you can compare directly. You can also test for a specific cell reference using the range parameter. The following tests for cell B10, specifically:
function onEdit(e) {
if (e.range.getA1Notation() == 'B10' && e.value > e.oldValue) {
Browser.msgBox("you increased the value")
}
}
Taking it even farther, you can specify a sheet within a Spreadsheet to test before the script will run.
function onEdit(e) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
if (e.range.getA1Notation() == 'B10' && e.range.getSheet().getName() == 'Página1' && e.value > e.oldValue) {
Browser.msgBox("you increased the value")
}
}
If the cell is not in the specified sheet or range,, nothing happens.
Watching a formula cell is a little more complex, but it will work. There are two methods in Apps Script. getValue() returns the value displayed in the cell, even if it's calculated by a formula. .getFormula() gets the cell's calculating formula. So, yes, this will work with simply calling .getValue() on the range.
The major difference is that you have to watch the formula cell because it is not directly edited by the user. In other words, the event object will not trigger if the cell value is calculated by a formula. Including it is easy:
function onEdit(e) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var formulaRange = e.range.getSheet().getRange("B4");
if (e.range.getA1Notation() == 'B10' || formulaRange && (e.range.getSheet().getName() == 'Página1' && e.value > e.oldValue) {
Browser.msgBox("you increased the value")
}
}
More on Apps Script event objects in the documentation.