0

Serge's solution here seemed like the way to go about this, but I'm a bit afraid that my circumstances may be too different...

I have a button where users can add a new set of rows with controls to a FlexTable, in order to allow them to insert a new member into a record set. After designing and building the app to do this (and despite assurances to the contrary), a requirement was then added for the users to be able to edit the record sets at a later date.

I've finally managed to get the data retrieved and correctly displayed on the Ui - for single member record sets. As a final stage, I am now attempting to extend this to accommodate record sets having more than one member. Obviously this requires determining how many members there are in the record set, and then adding the new rows/control group to the FlexTable, before loading the member into each control group.

So within this routine, (depending on how many members there are) I may need to trigger the same callback, which the user normally does with a button. However, the difference with Serge's fine example, is that his code triggers the checkbox callback at the end of his routine once all the Ui components are in place. My situation needs to do this on the fly - and so far I'm getting 'Unexpected error', which suggests to me that the Ui is not able to update with the added FlexTable controls before my code attempts to assign values to them.

Does anyone have any insight into this problem? Is my only recourse to completely re-build a fixed Ui and dispense with the dynamic rowset model?

Code follows -

1. event for adding controls:

var app = UiApp.getActiveApplication();
var oFlexGrid = app.getElementById('ExpenseDetail');
var oRowCount = app.getElementById('rowCount');
var oScriptDBId = app.getElementById('scriptDBId');
var iRows = parseInt(e.parameter.rowCount);      
var sVId = e.parameter.scriptDBId;

var vGridDefs = loadArrayById(sVId); //retrieve upload definition array from ScriptDB
var vControlNames = [];

if (isOdd(iRows)){
  var sColour = 'AliceBlue';
} else {
  var sColour = 'LavenderBlush';           
};

oFlexGrid.insertRow(0);
oFlexGrid.insertRow(0);
oFlexGrid.insertRow(0);
oFlexGrid.insertRow(0);

oFlexGrid.setRowStyleAttributes(0,{'backgroundColor':sColour});
oFlexGrid.setRowStyleAttributes(1,{'backgroundColor':sColour});
oFlexGrid.setRowStyleAttributes(2,{'backgroundColor':sColour});
oFlexGrid.setRowStyleAttributes(3,{'backgroundColor':sColour});   

var vExpenseDef = Get_NamedRangeValues_(CONST_SSKEY_APP,'UIAPP_GridExpense');
iRows = iRows+1;   

vControlNames = CreateGrid_MixedSet_(iRows, vExpenseDef, oFlexGrid, app);  
oRowCount.setText(iRows.toString()).setValue(iRows.toString());       

//SOME INCONSEQUENTIAL CODE REMOVED HERE, LET ME KNOW IF YOU NEED IT

vGridDefs = vGridDefs.concat(vControlNames); // unify grid definition arrays     
var sAryId = saveArray('expenseFieldDef', vGridDefs);
oScriptDBId.setText(sAryId).setValue(sAryId); //store array and save ScriptDB ID        

if (e.parameter.source == 'btnExpenseAdd'){
  hideDialog(); //IGNORE CHEKCBOX-DRIVEN CALLS
};

return app;

2. routine that calls the event

var app = UiApp.getActiveApplication();
var oPanelExpense = app.getElementById('mainPanelExpense');
var oPanelIncome = app.getElementById('mainPanelIncome');
var oPanelEdit = app.getElementById('mainPanelEdit');    

var chkExpenseAdd= app.getElementById('chkExpenseAdd');    
var bExpenseTrigger = e.parameter.chkExpenseAdd;

var sVoucherId = nnGenericFuncLib.cacheLoadObject(CACHE_EDIT_VOUCHERID);
var sVoucher = e.parameter.ListSearch1Vouchers;    

var aryVoucherInfo = getVoucherEditDetail(sVoucherId);     
//SAVE FOR RECORD MARKING CALLBACK
nnGenericFuncLib.cacheSaveObject(CACHE_EDIT_OLDRECORDS, JSON.stringify(aryVoucherInfo), CACHE_TIMEOUT);

sVoucher = nnGenericFuncLib.textPad(sVoucher, '0', 7);    
var bExp = (sVoucher.substring(0,2) == '03')

var oRowCount = app.getElementById('rowCount');
var iRowCount = parseInt(e.parameter.rowCount);

var sControlName = '';
var vControlVal = '';
var iExpIdx = 0;
var sControlType = '';
var oControl = '';
var vSummaryTotal = 0;

for (var iVal in aryVoucherInfo){    
  sControlName = aryVoucherInfo[iVal][2];
  vControlVal = aryVoucherInfo[iVal][3];

  switch (sControlName){
    case 'ESUM60': 
      vSummaryTotal = vControlVal;
      break;
    case 'EXUSRN': 
      continue; //DON'T OVERWRITE CURRENT USERNAME
      break;
  };            

  if (sControlName.indexOf('_')!=-1){ //TEST FOR CONTROL SET MEMBER      
    var aryControlSet = sControlName.split('_');

    if (parseInt(aryControlSet[1])>iRowCount){//*** TRIGGER THE EVENT ***          
      Logger.log(bExpenseTrigger + ' - ' + !bExpenseTrigger);
      chkExpenseAdd.setValue(!bExpenseTrigger, true);
      iRowCount = iRowCount +1;
    };

  };

  oControl = app.getElementById(sControlName);      
  var vCache = cacheSaveReturn(CACHE_UIEX_LISTS,sControlName);

  if (typeof vCache == 'undefined'){       

    oControl.setValue(vControlVal);
    oControl.setText(vControlVal);   
    //controlSetTextBox(oControl,vControlVal);
    //controlSetDateBox(oControl,vControlVal);  
  } else {

    if (!(nnGenericFuncLib.arrayIsReal(vCache))){
      vCache = JSON.parse(vCache); 
    };

    vCache = vCache.indexOf(vControlVal);        

    if (vCache != -1){
      oControl.setSelectedIndex(vCache);
    } else {
      controlSetListBox(oControl,vControlVal);
    };
  };

};

//SOME CODE REMOVED HERE

hideDialog();    
return app;
Community
  • 1
  • 1
Tim
  • 756
  • 1
  • 7
  • 12
  • UPDATE: turns out that it was working, just in its own sweet time, eg, the Ui updates well *after* the code executes meaning all calls to assign values to the new controls fail. I have now moved the triggering code into an event which precedes the routine where it is required, to ensure that the Ui has time to return. However I now need to find a clever way of knowing when that has taken place, so that the preloader dialog can complete... – Tim Apr 12 '14 at 13:42
  • 1
    Other side note be careful using parseInt in google apps script. It's much safer using new Number(). I'll spare you how I know this. – LennyZ71 Apr 14 '14 at 00:41
  • Thanks for the tip Lenny! Would be handy to know what conditions caused it to fail in your case though? – Tim Apr 14 '14 at 07:47
  • 1
    Tim, I was using it during a date conversion (http://stackoverflow.com/questions/15645343/how-to-use-timezone-of-calendar-to-set-timezone-for-date-object), but it has something to do with how it parses 8's, that was returning an undefined. And this link might explain it a little better. https://plus.google.com/112066118406610145134/posts/DwmPvk79awY – LennyZ71 Apr 16 '14 at 13:09

1 Answers1

0

Mogsdad to the rescue!

The answer (see above) for those at the back of the class (with me) is to simply pass the app instance parameter (e) to the event function, calling it directly from the main routine, thus keeping the chronology in step for when it returns the app to complete the routine. No need for the checkbox in this situation.

This only took me all day, but thanks Mogsdad! :)

Snippet below taken from 1/2 way down code sample 2 in the OP:

  if (sControlName.indexOf('_')!=-1){ //TEST FOR CONTROL SET MEMBER      
    var aryControlSet = sControlName.split('_');

    if (parseInt(aryControlSet[1])>iRowCount){      
      eventAddExpense(e); //THAT'S ALL IT TAKES
      iRowCount = iRowCount +1;
    };

  };
Community
  • 1
  • 1
Tim
  • 756
  • 1
  • 7
  • 12