Google-apps-script Client Validation

I recently saw a question on stackoverflow where someone was asking if it was possible to do a two-factor clienthandler to validate entries.  Since I've only been playing with apps scripts for a week (see my other posts on adventures with archiving Gmail - which I still have to finish up), I figured I'd give it a try to learn a bit more.

The scenario was he (or she) had two textboxes and wanted to allow a button to be clicked if either BOTH were empty OR BOTH had data.  My solution, while cumbersome, actually works.

I created two additional textboxes (which could be hidden) then piled on the validators on the original two texboxes. 

  • If entryTextbox1.length>0, validatorTextbox1.text=1 else validatorTextbox1=0,
  • If entryTextbox2.length>0, validatorTextbox2.text=1 else validatorTextbox2=0

Then some textchangehandlers

  • validatorTextbox1.onTextChange: if sum(validatorText1+validatorTextbox2)=1, don't allow click
  • validatorTextbox2.onTextChange: if sum(validatorText1+validatorTextbox2)=1, don't allow click

To handle databoxes, the theory is the same, but

  1. lengthchecks on dateboxes don't work properly they throw an error for some reason. i used a regex testing for the '-' character. you may need to change this if you've formated your date differently.  Or you could fully validate the date format.
  2. you need to fire events for invalid dates to handle empty fields
  3. and lastly, you need to use the valuechange handler instead as it was correctly pointed out it doesn't have onkeyup

Click Here to see the solution in action.


function doGet() {
var app = UiApp.createApplication();


var buttonDate = app.createButton('Click Me');
app.add(app.createLabel("This demonstrates using clienthandlers to require either neither or both dateBoxes to be populated before the button can be clicked"));
var validationWarning=app.createLabel("Please enter a value in both fields or neither field").setId('warningLabel').setStyleAttribute('color', "red").setVisible(false);
app.add(buttonDate);
app.add(validationWarning);
var panel = app.createVerticalPanel();


// db1.addValueChangeHandler(handler);

var db1=app.createDateBox().setId('db1').setName('db1');
var db2=app.createDateBox().setId('db2').setName('db2');
//Hide these two - but i left them visible so you can see the script in action.
var tbValidatorFordb1=app.createTextBox().setId('tb3').setName('tb3').setEnabled(false);
var tbValidatorFordb2=app.createTextBox().setId('tb4').setName('tb4').setEnabled(false);
var label1 = app.createLabel('The button was clicked.')
.setId('statusLabel')
.setVisible(false);


panel.add(label1);

var grid1=app.createGrid(2, 2);
grid1.setWidget(0, 0, app.createLabel("Field 1"));
grid1.setWidget(0, 1, app.createLabel("Field 2"));
grid1.setWidget(1, 0,db1);
grid1.setWidget(1, 1,db2);
panel.add(grid1);

var grid2=app.createGrid(2, 2);
grid2.setWidget(0, 0, app.createLabel("Validator 1"));
grid2.setWidget(0, 1, app.createLabel("Validator 2"));
grid2.setWidget(1, 0,tbValidatorFordb1);
grid2.setWidget(1, 1,tbValidatorFordb2);
panel.add(grid2);

app.add(panel);
//Set your validators for your first textbox

var clientHandler1=app.createClientHandler().validateMatches(db1,'\-').forTargets(tbValidatorFordb1).setText('1');
var clientHandler2=app.createClientHandler().validateNotMatches(db1, '\-').forTargets(tbValidatorFordb1).setText('0');

//Set your validators for your second textbox
var clientHandler3=app.createClientHandler().validateMatches(db2,'\-').forTargets(tbValidatorFordb2).setText('1');
var clientHandler4=app.createClientHandler().validateNotMatches(db2,'\-').forTargets(tbValidatorFordb2).setText('0');
//tb1.addValueChangeHandler(handler)
db1.addValueChangeHandler(clientHandler1);
db1.addValueChangeHandler(clientHandler2);
db2.addValueChangeHandler(clientHandler3);
db2.addValueChangeHandler(clientHandler4);
db2.addValueChangeHandler(clientHandler1);
db2.addValueChangeHandler(clientHandler2);
db1.addValueChangeHandler(clientHandler3);
db1.addValueChangeHandler(clientHandler4);

//Now create some client handlers to do the actual enable/disable of the button
db1.setFireEventsForInvalid(true);
db2.setFireEventsForInvalid(true);
var finalDisableValidator1=app.createClientHandler().validateSum([tbValidatorFordb1,tbValidatorFordb2], 1).forTargets(buttonDate).setEnabled(false).forTargets(validationWarning).setVisible(true);
var finalEnableValidator1=app.createClientHandler().validateNotSum([tbValidatorFordb1,tbValidatorFordb2], 1).forTargets(buttonDate).setEnabled(true).forTargets(validationWarning).setVisible(false);

db1.addValueChangeHandler(finalDisableValidator1);
db1.addValueChangeHandler(finalEnableValidator1);
db2.addValueChangeHandler(finalDisableValidator1);
db2.addValueChangeHandler(finalEnableValidator1);

//tb4.addChangeHandler(finalEnableValidator)
var handler = app.createServerHandler('myClickHandler').validateNotSum([tbValidatorFordb1,tbValidatorFordb2], 1);
handler.addCallbackElement(label1);
buttonDate.addClickHandler(handler);


//Code for textboxes

var button2 = app.createButton('Click Me');
var panel2 = app.createVerticalPanel();
panel2.add(app.createLabel("This demonstrates using clienthandlers to require either neither or both textBoxes to be populated before the button can be clicked"));
var validationWarning2=app.createLabel("Please enter a value in both fields or neither field").setId('warningLabel').setStyleAttribute('color', "red").setVisible(false);

panel2.add(button2);
panel2.add(validationWarning2);



// db1.addValueChangeHandler(handler);

var tb1=app.createTextBox().setId('tb1').setName('tb1');
var tb2=app.createTextBox().setId('tb2').setName('tb2');
//Hide these two - but i left them visible so you can see the script in action.
var tbValidatorForTb1=app.createTextBox().setId('tb5').setName('tb5').setEnabled(false);
var tbValidatorForTb2=app.createTextBox().setId('tb6').setName('tb6').setEnabled(false);
var label = app.createLabel('The button was clicked.')
.setId('statusLabel')
.setVisible(false);


panel2.add(label);

var grid3=app.createGrid(2, 2);
grid3.setWidget(0, 0, app.createLabel("Field 1"));
grid3.setWidget(0, 1, app.createLabel("Field 2"));
grid3.setWidget(1, 0,tb1);
grid3.setWidget(1, 1,tb2);
panel2.add(grid3);

var grid4=app.createGrid(2, 2);
grid4.setWidget(0, 0, app.createLabel("Validator 1"));
grid4.setWidget(0, 1, app.createLabel("Validator 2"));
grid4.setWidget(1, 0,tbValidatorForTb1);
grid4.setWidget(1, 1,tbValidatorForTb2);
panel2.add(grid4);

app.add(panel2);
//Set your validators for your first textbox
var clientHandler5=app.createClientHandler().validateLength(tb1, 1, 200).forTargets(tbValidatorForTb1).setText('1');
var clientHandler6=app.createClientHandler().validateLength(tb1,0, 0).forTargets(tbValidatorForTb1).setText('0');


//Set your validators for your second textbox
var clientHandler7=app.createClientHandler().validateLength(tb2, 1, 200).forTargets(tbValidatorForTb2).setText('1');
var clientHandler8=app.createClientHandler().validateLength(tb2,0, 0).forTargets(tbValidatorForTb2).setText('0');
//tb1.addValueChangeHandler(handler)
tb1.addKeyUpHandler(clientHandler5);
tb1.addKeyUpHandler(clientHandler6);
tb2.addKeyUpHandler(clientHandler7);
tb2.addKeyUpHandler(clientHandler8);
tb2.addKeyUpHandler(clientHandler5);
tb2.addKeyUpHandler(clientHandler6);
tb1.addKeyUpHandler(clientHandler7);
tb1.addKeyUpHandler(clientHandler8);

//Now create some client handlers to do the actual enable/disable of the button

var finalDisableValidator=app.createClientHandler().validateSum([tbValidatorForTb1,tbValidatorForTb2], 1).forTargets(button2).setEnabled(false).forTargets(validationWarning).setVisible(true);
var finalEnableValidator=app.createClientHandler().validateNotSum([tbValidatorForTb1,tbValidatorForTb2], 1).forTargets(button2).setEnabled(true).forTargets(validationWarning).setVisible(false);

tb1.addKeyUpHandler(finalDisableValidator);
tb1.addKeyUpHandler(finalEnableValidator);
tb2.addKeyUpHandler(finalDisableValidator);
tb2.addKeyUpHandler(finalEnableValidator);

//tb4.addChangeHandler(finalEnableValidator)
var handler = app.createServerHandler('myClickHandler').validateNotSum([tbValidatorForTb1,tbValidatorForTb2], 1);
handler.addCallbackElement(label);
button2.addClickHandler(handler);




return app;
}

function myClickHandler(e) {
var app = UiApp.getActiveApplication();

var label = app.getElementById('statusLabel');
label.setVisible(true);

app.close();
return app;
}