I've created a data validation in apps script for google sheets to prevent the red error flags from popping up. Now I need to control the message box wording. The code I wrote for the message box isn't working.
Here's what I have:
//set data validation
function myFunction() {
var range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('New CI').getRange('A1:AR');
var validation = SpreadsheetApp.newDataValidation().requireTextContains('Edits must be made in the google form').setAllowInvalid(false).build();
range.setDataValidation(validation);
}
//create browser box
function onEdit(e) {
if (
//range A1:AR5000
e.source.getSheetName() == "New CI" &&
e.range.columnStart >= 1 &&
e.range.columnEnd <= 45 &&
e.range.rowStart >= 1 &&
e.range.rowEnd <= 5000
) {
if (e.changetype === 'EDIT' && e.value !== 'Edits must be made in the google form');{
Browser.msgBox('Edits must be made in the google form');
}
}
}
Why don't you just share your spreadsheet in read only mode. And by the way there is no changeType in an onEdit() trigger event object and if you were using the change event it's spelled changeType. It's also not a good idea to put a dialog in an onEdit() simple trigger because they must complete in 30 seconds. So it might be better to consider a toast. But it looks like you don't want people to edit the spreadsheet so just share it with them in read only mode.
The Browser.msgBox works perfectly like this:
function onEdit(e) {
Browser.msgBox('Edits must be made in the google form');
}
In resume, your code runs perfectly, the problem that you are facing is either the if's conditions you have are never satisfied or you are not placing properly the trigger.
First, check the if conditions:
Is it correct the following if condition?
if (e.changetype === 'EDIT' && e.value !== 'Edits must be made in the google form');
What you are saying here is that you will only show the msgBox if the event value is exactly equal to Edits must be made.....
I do not think this is what you want, I believe that you want is that for any edit made on the spreadsheet the msgBox should appear. So, this should be enough:
if (e.changetype === 'EDIT');
Is this first if condition correct?
if (
//range A1:AR5000
e.source.getSheetName() == "New CI" &&
e.range.columnStart >= 1 &&
e.range.columnEnd <= 45 &&
e.range.rowStart >= 1 &&
e.range.rowEnd <= 5000
)
For me it looks okay if what you want is to show the msgBox only when someone edits the cells on the specified range and the specified spreadsheet 'New CI'.
Let me know if this solves your problem.
Related
I'm trying to write some code in Apps Script that triggers an email each time a condition is fulfilled in a Spreadsheet.
This spreadsheet contains the age of different transgenic lines of fish (the ages of the fish are automatically updated in the spreadsheet) and each of these transgenic lines has an associated caretaker with an email address. My idea is to trigger an automatic email using Apps script that is sent to the assigned caretaker each time one of these transgenic lines becomes older than 2 years old. However, I haven't been able to make it work yet. I'm not really sure which part of my code is preventing it from working properly.
Below I attach an example of how the spreadsheet would look like, as well as an example of the code that I've been trying to use (I'm a beginner when it comes to coding, so it's possible that there are many basic errors in it):
function fishalert() {
var subject = 'Fish aging alert';
var years = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("C2:C10").getValues();
if (years > 2){
for(r=2;r<20;r++){
var name = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange(r,1).getValue();
var emailaddress = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange(r,4).getValue();
var message = 'Line ' + name + ' is more than 2 years old';
MailApp.sendEmail(emailaddress, subject, message);
}
}
}
Sending Email when conditions are met by sampling once a day
function fishalert(e) {
if (e['day-of-week'] < 6) {//sends emails mon through fri between 9 and 10 in the morning
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet1');
const vs = sh.getRange(2, 1, sh.getLastRow() - 1, 4).getValues();
vs.forEach(r => {
if (r[2] > 2) {
MailApp.sendEmail(r[3], 'Fish aging alert', `Line ${r[0]} is more than 2 years old`);
}
});
}
}
Run this once:
function createTimeBasedTrigger() {
if(ScriptApp.getProjectTriggers().filter(t => t.getHandlerFunction() == 'fishalert').length == 0) {
ScriptApp.newTrigger('fishalert').timeBased().everyDays(1).atHour(9).create();
}
}
Time Driven Triggers
Time Driven Trigger Event Object
Class Range getValues() Method
For future enhancements
you will probably only want to send these emails on a less frequent schedule and probably only once when the threshold is achieved and you'll probably want to collect one email for each unique email address. But this is an answer to your current question
I have implemented a Kendo line chart using ASP.NET. As in the kendo examples shown i have used series{} to display lines. I have written my own custom template in series.Line to display the labels for markers when mouse hovered.But i need to pass two parameters to that function.
series.Line(m => m.Values[0].value).Labels(lbl => lbl.Position(ChartPointLabelsPosition.Above).Visible(true).Template("#= formatLabelForEffort(category#"))
In the above code formatLabelForEffort() is the function call written in the template which has only one arguement called category. I want to send another arguement in the same function. The second arguiement would be an array. How would i achieve this. Please help me out to resolve this issue. Thank you in advance.
I have prepared a demo of this for you:
custom template passing in options.
All you need to do is change your existing function from:
.Template("#= formatLabelForEffort(category)#")
to
.Template("#= formatLabelForEffort(category, dataItem)#")
as long as the array is part of the original data model that is being bound within the series you should be able to alter your function to handle the code.
then using my version of your function:
function formatLabelForEffort(category,dataItem){
var retString = '';
console.log(dataItem);
if (dataItem.myArray !== undefined &&
dataItem.myArray !== null &&
dataItem.myArray !== null &&
dataItem.myArray.length > 0)
{
retString = 'Category is:: ' + category + '\r\n' ;
retString += kendo.stringify(dataItem.myArray) ;
}
else
{
retString = 'Category is:: ' + category;
}
console.log(retString);
return kendo.htmlEncode(retString);
}
we simply check to ensure the array is present and then return a string accordingly.
for more info on what you can pass to the series labels check this link:
series label template configuration
any further issues give me a shout and I will expand where I can.
I have a observable in my function and i just want to apply required validation to it . so i done like this
View model:
self.textareadata = ko.observable().extend({required:true});
var errors = ko.validation.group(self.textareadata);
On button click i am trying to fire up the validation like
self.click = function()
{
if(errors().length <=0)
{//post data}
else
{errors.showAllMessages();}
}
// i tried errors.length but its failing me i.e length always zero .
Using the above if condition on button click if i dont have any data in my text area control i get the message this field is required but when i enter something(automatically messages clear's) and again click means here i am stuck i.e else condition firing not expected if .
So debugged and checked i.e i can find a index[0] of null value . This i making my if condition fail .
I try validatedobservable if i use multiple observable validation but as it's a single one i want to do it by group .
Fiddle attached click Here
Anything i'm missing here & Help is appreaciated .
You need to provide array in group
self.errors = ko.validation.group([self.textareadata]);
Then use it like this
self.click = function()
{
if(self.errors().length == 0)
{
//post data
}else{
errors.showAllMessages();
}
}
I have a long running database update that I want to report on while it is happening.
I prime the interval time to fire at
I have seen lots of examples of problems with setInterval and it looks simple, but I cannot get it to fire with this code. The checkSaveStatus code pulls back a status report that tells how many rows have been saved. I will enhance it once I to display the results once I can get the basic functionality working.
I am beginning to suspect that this may have something to do with the way I am handling the Ajax call.
var checkTimer;
function checkSaveStatus() {
var saveStatus = ajaxCall("WebServices/FLSAService.asmx/CheckFLSASaveStatus");
if (saveStatus == null) {
clearInterval(checkTimer);
return;
}
log('saveStatus: ' + saveStatus.InputCount + '/' + saveStatus.ResultCount + ':' + saveStatus.SaveComplete, 'FLSA');
if (saveStatus.SaveComplete) {
reportSaveComplete = true;
clearInterval(checkTimer);
}
}
checkTimer = window.setInterval(checkSaveStatus, 1000);
... make an asynchronous Ajax call that takes a long time to complete
... the checkSaveStatus code doesn't fire until the Ajax call completes
As far as I know, you cannot return a value from your ajax because it is asynchronous. The way that is handled is through callbacks. So this section of code:
var saveStatus = ajaxCall("WebServices/FLSAService.asmx/CheckFLSASaveStatus");
if (saveStatus == null) {
clearInterval(checkTimer);
return;
}
Is more than likely not operating properly. It is hard to say without seeing the ajaxCall function, but you are probably not getting the result you intended to get. That left hand side assignment is not going to reflect anything which would be asynchronous. As a result it will either be what the default value is returned from ajaxCall, or it will be undefined, which when compared with == null will be true, and in turn will clear your interval and return.
I'm having a colModel entry like this:
{name:'status', index:'status', sorttype:"text", xmlmap:"STATUS", width:"90", stype: 'select', searchoptions:{sopt: ['eq','ne'], value:':all;Hold:Hold;4-Eye-Check:4-Eye-Check;Approved:Approved;Rejected:Rejected;Closed:Closed'}},
thats working fine as long as it's used in the FilterToolBar, but if I open the NavGridSearch Im running into troubles. The entry "all" is not working anymore. The query in the FilterToolBar seems to ignore my empty but the NavGridSearch doesn't.
Is there any wildcard sign which could be used instead of an empty String, which delivers all entries regardless if I search for all status entries in the FilterToolBar or the NavGridSearch?
I use the newest OpenSource jQGrid Lib(4.3.2)
Thanks in advance!
I am trying to understand the use case for a criteria of 'all'. It seems the purpose of 'all' is that you want the grid to ignore this search criteria and return all rows regardless of this value? If that is the case, why would the user even want to select this search criteria - they can just remove it and the same effect will be achieved. Or am I missing something?
Update
The grid uses function createEl : function(eltype,options,vl,autowidth, ajaxso) to create the select for the search form. Unfortunately this select always adds the all criteria to the list, even though it has a search value of "" which will not match any rows. One workaround is to modify the grid to skip select options that have an empty value. Using the source file jquery.jqGrid.src.js you can add the following code to skip the all option:
else if (sv.length > 1 && sv[0].length === 0) continue;
Here it is within the context of createEl:
if(typeof options.value === 'string') {
so = options.value.split(delim);
for(i=0; i<so.length;i++){
sv = so[i].split(sep);
if(sv.length > 2 ) {
sv[1] = $.map(sv,function(n,ii){if(ii>0) { return n;} }).join(sep);
} else if (sv.length > 1 && sv[0].length === 0) continue; // <-- Change this line
ov = document.createElement("option");
ov.setAttribute("role","option");
ov.value = sv[0]; ov.innerHTML = sv[1];
elem.appendChild(ov);
if (!msl && ($.trim(sv[0]) == $.trim(vl) || $.trim(sv[1]) == $.trim(vl))) { ov.selected ="selected"; }
if (msl && ($.inArray($.trim(sv[1]), ovm)>-1 || $.inArray($.trim(sv[0]), ovm)>-1)) {ov.selected ="selected";}
}
If you need a minified version, update the "src" version of jqGrid and then run it through the Google Closure Compiler.
I am not sure this is a general purpose change, which is why I am calling it a workaround for right now. Longer term a better solution needs to be found so jqGrid can be patched... I'll try to find some more time later to revisit this issue.
Does that help?