It’s been awhile since I have written anything to do with Google Apps Script, so I decided to start exploring some features of the Apps Script world that are new to me.
When I first got started with Apps Script almost 6 years ago, there were lots of things I wanted to do, but just didn’t yet have the skills to figure out or tried but ran into a limitation on the Apps Script platform. Now that Apps Script has matured, it’s time to revisit some of those early desires.
In this tutorial, we’re going to look at a workflow that should have a large swath of utility for people who read this site. We’re going to take a Google Form submission and then use that data to populate a custom template in Google Docs.
To get started, we’ll need to create a Google Form to accept submissions and a Google Doc to act as our template.
For this example, I just created a basic form to accept some data that looks like this:
As you can see nothing fancy going on here. I created a spreadsheet to accept the responses, as that is ultimately where our code will live that will generate the Google Doc.
After creating the form and the spreadsheet, I created a Google Doc in the same folder that will serve as our template document. Every time a form is submitted to the spreadsheet, our script will make a copy of this template and replace parts of the text with the data submitted to the form.
As you can see, this is a pretty basic document, but since we are just making a copy, your template could include lots of different styling, images, etc. if you so desire.
There are a few important things to note about this template though. There are several ways to populate a Google Doc using Google Apps Script. One way is to programmatically create the document using code, meaning we would add the different document elements one by one using a script.
While that is possible, I found it very difficult to do, especially when trying to create a document with an kind of sophistication or styling.
The other way involves the strategy we’re taking here. We make a document that acts as a template, then we look for certain pieces of text within the template document, then replace those pieces of text with live data when our form is submitted.
However, since we’ll need to search all of the text in the document to replace it, it is best to make the text you want to replace unique in the document. To do that, you should use what are known in the web development world as replacement tags. In the example above, I’ve surrounded the names of the fields I want to replace in double curly braces like so {{Field to Replace}}.
While that is only one possibility, there are a few others that are commonly used:
{{Field to Replace}} %Field to Replace% #Field to Replace# [[Field to Replace]]
At the end of the day, really all we are focused on is creating a unique tag that we can find later. Using some sort of character as brackets helps with this since two curly braces is an unlikely pattern to find elsewhere.
Another best practice here, just for you own sanity, would be to make sure that the text inside your brackets matches the header in the spreadsheet.
Now that we’ve got everything setup, we can look at the code that will make all of this happen. For those unfamiliar with how to write a basic Google Apps Script project file, you might want to check out an earlier post where I go over the process from beginning to end.
For this tutorial, I’ll assume that everyone knows how to open Tools > Script Editor to edit a script file and set a form submission trigger on a particular script.
The code below is fairly well commented to show the intent of each line, so I won’t rehash a bunch of it here:
function autoFillGoogleDocFromForm(e) { //e.values is an array of form values var timestamp = e.values[0]; var firstName = e.values[1]; var lastName = e.values[2]; var title = e.values[3]; //file is the template file, and you get it by ID var file = DriveApp.getFileById('your_file_id_here'); //We can make a copy of the template, name it, and optionally tell it what folder to live in //file.makeCopy will return a Google Drive file object var folder = DriveApp.getFolderById('your_folder_id_here') var copy = file.makeCopy(lastName + ',' + firstName, folder); //Once we've got the new file created, we need to open it as a document by using its ID var doc = DocumentApp.openById(copy.getId()); //Since everything we need to change is in the body, we need to get that var body = doc.getBody(); //Then we call all of our replaceText methods body.replaceText('{{First Name}}', firstName); body.replaceText('{{Last Name}}', lastName); body.replaceText('{{Title}}', title); //Lastly we save and close the document to persist our changes doc.saveAndClose(); }
Some things to note about this project, myFunction is what gets called when the form submission trigger runs, and it passes in the form values as the ‘e’ parameter to the function. This script makes use of both the DriveApp and DocumentApp classes of Google Apps Script, so be careful when writing your own version to distinguish between what is a Google Drive file and what is a Google Document.
Now that we have all scripts and Drive resources in place, we need to add a trigger to our project so that the script runs whenever a form is submitted. To do that, we can navigate to the ‘Edit’ menu in the script editor, and then click on the “Current project’s triggers” option.
The trigger editor will open up in a new window and show us all of the triggers associated with a project if there are any. To add a trigger, click the ‘Add Trigger’ button in the bottom right of the screen, which will open up a modal menu with options for setting the trigger.
First, we want to choose the function that will get triggered, in this case ‘autoFillGoogleDocFromForm’ will get selected. We can leave the deployment as ‘Head,’ unless you know what you are doing here. Then, we want to select ‘From spreadsheet’ as our event source and “On form submit” event type. All of those settings ensure that the correct data gets passed to our script when it is triggered.
The last setting, which is an optional recommendation, determines how often you are notified of errors. My recommendation is set that to notify you immediately. Once you’ve sorted that, we can click save and our trigger will be active. You can give it a try by submitting a test form.
It took me a while to figure this difference out, but it can be summed up like this: all Google Docs are Drive files, but not all Drive files are Google Docs. Google Drive controls things about the file, like where it lives, who has access to it, and how we download it, but to make changes to a file that is a Google Doc we need to operate on it using the DocumentApp class.
After submitting our test form, we end up with a Google Document in the folder we specify that looks like this:
While this is a pretty simple example, I foresee lots of use cases for a workflow like this, from creating certificates to populating invoices or purchase orders, this should be another helpful tool to keep in your Google Apps Script tool belt.
After over 100+ comments on this post and more on YouTube, I’ve created a few tutorials that branch off of things that people may want to do:
Auto Fill a Google Doc Template from Google Sheets Data
This tutorial is very similar to using a Google Form to trigger the automation, but instead we runt the automation from a menu item and pull data from an existing Google Sheet. We also write the URL of the created document back to the sheet.
Replacing Text with a Clickable URL
Many people have asked how they can replace the text in their Google Doc templates with a clickable link. The tutorial linked above shows how you can modify this script to insert links into your templates.
Hi Jeffrey,
First, your YouTube tutorial on auto filling a google doc from a form, in my opinion, is the best.
Question:
How would you go about emailing the automatically created document if the form had an email field?
In other words, how would you email that document to the person who submits the form if an email field was available?
Hey Alx,
Thanks so much for the kind words!
What you want to do should be straight forward. After following the steps in this tutorial, basically calling saveAndClose on the doc after replacing the text, you will want to get the Drive file representation of that document using the file id again, than get that as a particular MIME type (PDF, Word Doc, etc.).
From there, you can just attach that file as an email. I have another tutorial on using Gmail attachments that covers some of that.
Let me know if you have any questions as you start to implement.
Thanks for watching and reading 🙂
JE
Thanks Jeffrey, lucid, helpful, concise – relatively rare qualities among those writing about Appscript! This also happens to very relevant for something I’m thinking about (even the email question, great!).
I’m guessing the owner of the form would be owner of the doc, but the recipient of the email could make a copy that they’d own? I could try and see, but there are rabbit holes, as you talked about in a post.
I’ll send you an email about the project, you might be interested. Thankx
Thanks for the kind words! Glad the post was of some use to you. Whoever authorizes the script to run will be the owner of the form, but depending on the sharing settings the recipient may/may not be able to make a copy. What I typically do in situations like this is use the file.getAs method to get the doc as a PDF, then attach that to the email. That way the person has a read-only copy. Let me know if you have any questions as you start working through your project. Thanks for reading, JE
Is there a way to download a copy to your computer? For instance. When I hit submit I want it to populate a google doc and download the copy to my computer or into an email. Thanks!
I think you would have to create a custom HTML interface to trigger a download. To send it in an email, you could just get the link to the document after you fill it with data and send that in an email body. You can look at some examples on how to send an email in this tutorial. Thanks for reading, and if you want to post code samples I’m happy to take a look.
Hi Jeffrey, I’m having a problem opening the Google Doc file I’ve created which exists in the same folder as the form, spreadsheet for answers containing the script. My file name is ‘Online Protest Form’ and it’s a Google Doc.
I’ve tried your method:
DriveApp.getFileById(‘Online Protest Form’)
as well as:
DocumentApp.openById(‘Online Protest Form’)
Is there a file extension that I’m missing?
Thanks
Angelo
Hi Angelo,
Thanks for reading. If you look at the URL in the browser address bar when you have the Google Doc open, you’ll notice a long string of alphanumeric characters: https://docs.google.com/spreadsheets/d/12GHL3cVgaq8343433434343434xwwOWUdL8FdT_-usoaEI3YmLUkY
All Drive file types will have an ID like this stored somewhere, typically after the /d/ in the URL. If you copy and paste just that part of the URL into the code, you should get different results.
Thanks for reading and be sure to update this thread if things still aren’t working.
Cheers,
Jeff
Bingo! .. thanks.
Jeffrey, I wanted to return the favor. Here is a version of your script that parses the header of the linked-spreadsheet and will automatically look for any form-field in the doc and replace. Therefore it’s generic and be used nearly automatically. It also has a no-answer handler built-in. – Thanks! Ang
function onFormSubmit2(e) {
var answers = e.values;
var QandA = e.namedValues;
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName(‘Form Responses 1’);
var headers = sheet.getRange(1,1,1,sheet.getLastColumn()).getValues();
//file is the template file, and you get it by ID
var file = DriveApp.getFileById(‘your file ID’);
// We can make a copy of the template, name it, and optionally tell it what folder to live in
// file.makeCopy will return a Google Drive file object
var folder = DriveApp.getFolderById(‘your folder ID’)
// use required fields to build file name
var copy = file.makeCopy(QandA[‘Required-1’] + ‘-‘ + QandA[‘Required-2’] + ‘-‘ + QandA[‘Required-3’] ,folder);
// var copy = file.makeCopy();
//Once we’ve got the new file created, we need to open it as a document by using its ID
var doc = DocumentApp.openById(copy.getId());
//Since everything we need to change is in the body, we need to get that
var body = doc.getBody();
for (var i=0; i < headers[0].length ; i++){
if(QandA[headers[0][i]] != ''){
body.replaceText("{{" + headers[0][i] + "}}", QandA[headers[0][i]]);
} else {
body.replaceText("{{" + headers[0][i] + "}}", ' ');
};
}
//Lastly we save and close the document to persist our changes
doc.saveAndClose();
PS .. the one improvement I need to make ..
When you build a form/questionaire, having a “?” is very common. Unfortunately, the “?” is used as a wildcard in body.replaceText . Therefore, currently I’ve replaced my “?” with “\?” in my Google Form, so that it will find just the “?” in the Google Doc.
For instance, my GF might have a question…
Are you married\?, which gets appended to {{Are you married\?}} in the body.replaceText statement.
… and in the Google Doc, it put {{Are you married?}}.
This works.
It would be nice to put another if/else statement in there that looks at headers[0][i] and looks for a “?” and replaces it in-place with a “\?”.
If we did that, it would be a truly universal script.
Thoughts?
Hi Jeffrey,
I am looking to add below functionality in my google form:
I need multiple answers to be entered and recorded as per user’s choice. For ex: if user wants to add 1 phone number he enter 1 , 3 phone numbers he eters 3 different numbers and so on..
Would appreciate if anyone can help me with this.
Hi Mohit,
It sounds like you want to make a Google Form with dynamic inputs, where I click a plus button and get another space to add a phone number as many times as I want. Does that sound accurate?
If so, unfortunately there isn’t a way that I know of to make that experience using Google Forms. When I’ve done things like that in the past, I’ve always set an upper limit on the number of response they can provide and make all fields beyond the first one optional. So, if they can submit up to five phone numbers, make 5 phone inputs with only the first one required.
App Maker might let you get a bit more fancy with the interface, but I don’t know that for sure.
You last ditch effort would be to create a custom HTML form that is served by Google Apps and ties into a spreadsheet. This would allow you to design the interface anyway you want, as long as you can develop a spreadsheet to capture the data in a structured way. However, I’d say this is out of reach for most people without a significant amount of experience with HTML and JS.
Let me know if there are other details you can provide that might help us find a solution for you.
Thanks for reading,
Jeff
Hi Jeff, thank you for this awesome article. I’m trying to do this for automation in some tasks in my company. One question that I need to ask you: Do you know how to replaceText in Spreadsheet or Slide? I check the Apps Script Reference for Spreadsheet and Slide but I can’t find anything similar to Document Body replaceText(…) function. Thank you very much.
Hi Hoang,
See this link regarding the Slide.replaceAllText method of the Slide class. That should be what you are looking for, as I know it is possible to replace text in a slide as I’ve done it in other project.
Thanks for reading,
Jeff
Hi Jeffrey! Tahanks a lot. Your tutorial had been a great help for a starter like me. Anyway, can you recommend a book to read about this kind of thing? about coding on google spreadsheet and other google doc platform?
Not sure I’m aware of any books, but Ben Collins offers some great stuff if you are interested in some more structured materials.
This post was very helpful. Thank, you. Somewhat related to your post regarding auto-creating and filling a new doc using submitted form data, is it possible to do the same thing with sheets? In other words, is it possible to use apps script to auto-create and fill a new spreadsheet with select data from a form submission?
Hey Todd,
Thanks for reading. It does indeed look possible using the SpreadsheetApp.create method to create a new spreadsheet. It seems like from there you’d need to use the
Spreadsheet
class that gets returned to add the data.Cheers,
Jeff
To clarify, just like your post related to creating documents, I want to create a new spreadsheet for each new entry.
Greetings, Jeff,
This tutorial is nothing less than amazing! I cannot express enough how grateful I am that you choose to use your talents in this way. I was tasked to develop a google form that would create and email a document (pdf) in order to streamline a very cumbersome, time consuming process at work. I have to admit, I had absolutely no idea what I was doing. My knowledge of any sort of coding or scripts is rudimentary (at best). I figured there had to be a way, so I began researching. I was thrilled to find your tutorial. My google form/email/pdf attachment is working so well, other departments want a similar product. Thank you so very much.
Hi Beth,
Thank you for the thoughtful reply. I’m so glad that my post was helpful to you. Be sure to check back as I’ll continue to add content related to Google Apps Script.
Thanks for reading,
Jeff
This is awesome Jeff and so close to what I am looking for!! I just need to add one component that I’m having trouble with. I would like the document to be created from the google sheet AFTER a vlookup has been run from the form responses. Does that make sense? How do I add that step in the script? I tried adding a macro to copy the vlookup formula after the response is submitted, but it’s not working.
Any help/suggestions would be great. I cannot do any “Add-ons” so I need to do this via script and I’m not as cool as you :).
I’m wondering too if a macro would work? Have the macro run before the new doc is created to include the additional information? Where would I put that in the script??
Thanks
My inclination would be to translate the steps in the macro to a function in Google Apps Script. Can you describe what is happening in the macro?
In my example, the doc is created directly from the form event input, but there may be another trigger like onEdit that could be more applicable. I’m also really not that good with spreadsheets LOL, so I’m not that cool : )
Thanks for reading!
Every time I trigger from my form, I get this error, Access denied: DriveApp. at myFunction(line)
Any reason why this would be happening?
This would suggest that you don’t have adequate privileges on the Drive folder/file you want to operate on. I’d check that first and comment back if that isn’t the issue.
Awesome tool to automate certain processes, however, I must be doing something wrong and as I am not getting any error messages, I don’t know where to look for the issue.
When I fill out the form, nothing changes in the folder ie the new Doc file doesn’t create.
If I try to run the script in the script editor window, I get this error “TypeError: Cannot read property “values” from undefined. (line 3, file “Code”)” which I understand is dur to the fact I run it from within the script editor and is not triggered by a form submission.
Any help is highly appreciated!
Can you post the contents of your script? That generally is the only way I can help troubleshoot.
JE
Sure, thanks so much! Here is the complete script:
function myFunction(e) {
//e.values is an array of form values
var timestamp = e.values[0];
var firstName = e.values[1];
var lastName = e.values[2];
var title = e.values[3];
//file is the template file, and you get it by ID
var file = DriveApp.getFileById(‘https://docs.google.com/spreadsheets/d/158ifecBYqhn0AeRYJBIquh1PO_g5ciSyKnizoV_15NA/edit#gid=217946722’);
//We can make a copy of the template, name it, and optionally tell it what folder to live in
//file.makeCopy will return a Google Drive file object
var folder = DriveApp.getFolderById(‘https://drive.google.com/drive/u/1/folders/1aw5GHihJLxEj702Kx3eW1pVwlmR9Pmli’)
var copy = file.makeCopy(lastName + ‘,’ + firstName, folder);
//Once we’ve got the new file created, we need to open it as a document by using its ID
var doc = DocumentApp.openById(copy.getId());
//Since everything we need to change is in the body, we need to get that
var body = doc.getBody();
//Then we call all of our replaceText methods
body.replaceText(‘{{First Name}}’, firstName);
body.replaceText(‘{{Last Name}}’, lastName);
body.replaceText(‘{{Title}}’, title);
//Lastly we save and close the document to persist our changes
doc.saveAndClose();
}
Ok, thanks! The first thing I can see is that you are using full URLs to docs and folders instead of the ids; for example, in the first call to
DriveApp.getFileById
you pass in this url:https://docs.google.com/spreadsheets/d/158ifecBYqhn0AeRYJBIquh1PO_g5ciSyKnizoV_15NA/edit#gid=217946722’
The id is actually just the part after the /d, so this alphanumeric string
158ifecBYqhn0AeRYJBIquh1PO_g5ciSyKnizoV_15NA
.Try to address those two points, run the script again and let me know if you get different results.
Thanks for reading, Jeff
Thanks Jeff, that might have been the issue, but I also recreated the trigger and now it’s working flawlessly!
Thanks once again!
Nikola
Awesome! Glad I could be of some assistance, and thanks for reading!
Cheers, Jeff
I have just spent two sleepless weeks looking for a replacement service to do this for a company I work for.
We want to replace our current services since when there is any issue they point fingers at each other and nothing gets resolved so business stops.
I don’t know if I can figure out the code to do this but you sure make it seem possible.
Would sure like to attempt it but I know I’m going to need some help…. but not as much as I need sleep.
Oh and can this work for 2 documents to be created from the same data in the sheet?
Hi Lalla,
Thanks for reaching out. This can work for really as many documents as you want. I’ve created a lot of setups like this for other business that function pretty much at 100% most of the time using very little resources. If you’d like to explore what that could look like for your company, send me an email at jeffeverhart383@gmail.com so we can discuss further.
If you want to go it alone, feel free to post some code snippets here if you run into trouble.
Regards,
Jeff
Sent! Thank you so much!
I know ZERO about scripts, but this worked like a CHARM for me and I am so happy my heart is racing! This is just what I needed to help make our school’s process for adding events to the master calendar a simple and seamless process. THANK YOU!