Search code examples
google-apps-scriptweb-applicationsasynccallback

Google Apps Script - Callback for class methods used in webapp


I have a GAS that i'm submitting a form in my webapp to. It does the following:

  1. opens a template doc containing placeholders (in the format of ##form_field_name##) corresponding to the names of the form fields on the form
  2. makes a copy of the template and saves it to the appropriate location
  3. iterates through the submitted values and does a body.replaceText('##' + key + '##', val); to populate the values into the doc
  4. does a body.replaceText('##.*##', ''); to get rid of the place holder strings for any fields left blank (as they are not currently submitted with the form)

I'm ending up with a document that looks like the user did not submit ANY data at all. My guess is that it's because the #4 is running before #3 finishes. Is there any way to specify #4 as a callback? Something like:

body.replaceText('##' + key + '##', val, () => {
    body.replaceText('##.*##', '');
});

Here is the code for the function that does the replaceText()ing:

/**
 * Iterate through obj of keys/values and replace ##key## 
 * with value in doc 
**/
function doFindAndReplace(obj, doc) {
  try {
    //body = doc.getBody();
    var id = doc.getId();
    var _doc = DocumentApp.openById(id);
    var body = _doc.getBody();
    //var doc = _doc;
    //Logger.log(body);
    for (var prop in obj) {
      if(obj.hasOwnProperty(prop)) {
        var val = obj[prop];
        var key = prop.indexOf('intake') === 0 ? prop.toUpperCase() : '';
        //Logger.log(key + ' = ' + val);
        //Logger.log(typeof val);
        body.replaceText('##' + key + '##', val);
        _doc.saveAndClose();
        var _doc2 = DocumentApp.openById(id);
        body = _doc2.getBody();
        body.replaceText('##.*##', '');
      }
    }
  } catch(e) {
    Logger.log('doFindAndReplace() | ' + e);
    errors.push({ 
      fxn: 'doFindAndReplace', 
      msg: e, 
      custom: 'id=' + id
    });
    return false;
  }
  return doc;
}

The parameter obj is an object containing the submitted form data and doc is a reference to the newly created document in which the find-and-replace is taking place.

If nothing like that is possible, I was thinking about adding an onOpen trigger that wold run the #4 (body.replaceText('##.*##', '');). This would require that the user opens the doc for the first time while online and a huge part of the use case is using the newly creatted document offline.

Can anyone suggest another workaround?


** **EDIT** **
I updated my code (reflected above) to `saveAndClose()` and then re-open before running the 2nd `replaceText()`. I get the following error:

Document is closed, its contents cannot be updated

I know, you'd think it would come after the saveAndClose(). But thta's the line the error points at and even if I remove the rest of the lines after it, the same error occurs.

If I remove the saveAndClose() line with the code as is, I get my original problem again.

I don't think this is the same issue as Weird Behaviour with DriveApp.getFileById()


** **EDIT 2** **
Here's an example of an object passed to the function:
{
intake_activity_level: "Normal",
intake_assessment_comments: "",
intake_client_addr: "1600 Fake St XXXX, XX 12345",
intake_client_email: "[email protected]",
intake_client_name: "Dan Tester",
intake_client_phone: "(917) 555-1212", 
intake_consent: "yes", 
intake_consent_owner: "Jim David",
intake_food_intake: "Decreased",
intake_gen_food: "",
intake_gen_health: "sit dolam amet",
intake_gen_meds: "re et sapien et, consectetur rhoncus lacus. Aliqua Vivamus ipsum diam, venenatis a turpis eget, volu",
intake_gen_reasons: "Lorem ipsum",
intake_gen_vaccine: "Yes",
intake_misc_comments: "",
intake_misc_descriptor: "wood",
intake_panting: "Increased",
intake_pet_breed: "Pug",
intake_pet_dob: "8/11/2015",
intake_pet_name: "FLuffy",
intake_pet_sex: "Spayed Female",
intake_pet_species: "Dog",
intake_pet_weight: "140lbs",
intake_sleep: "Increased",
intake_source: "Sally Balls",
intake_stiffness: "Normal",
intake_symptoms: "coughing, belching or gas",
intake_vet: "Urban Vet",
intake_voice: "Increased",
intake_water_intake: "Decreased"
}

and a watered-down version of the Document can be accessed here.


Solution

    • You want to replace '##' + key + '##' to va; with body.replaceText('##' + key + '##', val); using obj.
    • You want to replace ##.*## to '' with body.replaceText('##.*##', '');.
    • You want to run body.replaceText('##.*##', ''); after all body.replaceText('##' + key + '##', val); were run.

    If my understanding is correct, how about this answer?

    In your current script, body.replaceText('##.*##', ''); is put in the for loop. By this, only intake_activity_level: "Normal", is used and other objects are not used. In this case, after the 1st body.replaceText('##' + key + '##', val); is run, all ##key## values are removed by body.replaceText('##.*##', '');. I thought that this might be the reason of your issue.

    Modified script:

    When your script is modified, please modify as follows.

    From:
    for (var prop in obj) {
      if(obj.hasOwnProperty(prop)) {
        var val = obj[prop];
        var key = prop.indexOf('intake') === 0 ? prop.toUpperCase() : '';
        //Logger.log(key + ' = ' + val);
        //Logger.log(typeof val);
        body.replaceText('##' + key + '##', val);
        _doc.saveAndClose();
        var _doc2 = DocumentApp.openById(id);
        body = _doc2.getBody();
        body.replaceText('##.*##', '');
      }
    }
    
    To:
    for (var prop in obj) {
      if(obj.hasOwnProperty(prop)) {
        var val = obj[prop];
        var key = prop.indexOf('intake') === 0 ? prop.toUpperCase() : '';
        body.replaceText('##' + key + '##', val);
      }
    }
    body.replaceText('##.*##', '');
    
    • In this modification, at first, body.replaceText('##' + key + '##', val); is run in the for loop. And then, body.replaceText('##.*##', ''); is run at the outside of the for loop.

    Note:

    • If saveAndClose() is required, how about putting doc.saveAndClose() after body.replaceText('##.*##', '');?