Apps 脚本:使用来自 Google Sheet 的数据行填充模板。如何让它更有活力?
Apps Script: Fill template with rows of data from Google Sheet. HOW to make it more dynamic?
我有一个脚本,它用 Google Sheet 中每一行的数据替换 Google 文档模板中的每个 {{Keyword}}。 (下面的示例 sheet)
Name
Address
City
Document Link
Name 1
Address 1
City 1
the script writes the new doc URL here
Name 2
Address 2
City 2
the script writes the new doc URL here
这是我目前运行(成功)的代码:
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu('Documents & Mail Merge')
.addItem('Generate Documents', 'createNewGoogleDocs')
.addSeparator()
.addToUi();
}
function createNewGoogleDocs() {
const documentLink_Col = ("Document Link");
const template = DriveApp.getFileById('templateIdGoesHere');
const destinationFolder = DriveApp.getFolderById('destinationFolderIdGoesHere');
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('data');
const data = sheet.getDataRange().getDisplayValues();
const heads = data[0]; // Assumes row 1 contains the column headings
const documentLink_ColIndex = heads.indexOf(documentLink_Col);
data.forEach(function(row, index){
if(index === 0 || row[documentLink_ColIndex]) return;
const templateCopy = template.makeCopy(`${row[0]} ${row[1]} Report`, destinationFolder); //create a copy of the template document
const templateCopyId = DocumentApp.openById(templateCopy.getId());
const templateCopyBody = templateCopyId.getBody();
templateCopyBody.replaceText('{{Name}}', row[0]);
templateCopyBody.replaceText('{{Address}}', row[1]);
templateCopyBody.replaceText('{{City}}', row[2]);
templateCopyId.saveAndClose();
const url = templateCopyId.getUrl();
sheet.getRange(index +1 , documentLink_ColIndex + 1).setValue(url);
})
}
我想实现的功能/更改的功能:
将硬编码的 {{Placeholders}}(例如 templateCopyBody.replaceText('{{Name}}', row[0]);
)替换为另一种将任何列名视为模板文档中潜在 {{Placeholder}} 的方法。
所以基本上 我应该可以自由地编辑、添加、移动或删除 sheet 中的列,而不必再对它们进行硬编码,而只是调整模板。
可能有帮助,我发现了一种类似的脚本,它使用 Gmail 草稿作为模板而不是 Google 文档,根据我的理解,这里有 2 个函数可以实现我需要的功能:
/**
* Fill template string with data object
* @see
* @param {string} template string containing {{}} markers which are replaced with data
* @param {object} data object used to replace {{}} markers
* @return {object} message replaced with data
*/
function fillInTemplateFromObject_(template, data) {
// We have two templates one for plain text and the html body
// Stringifing the object means we can do a global replace
let template_string = JSON.stringify(template);
// Token replacement
template_string = template_string.replace(/{{[^{}]+}}/g, key => {
return escapeData_(data[key.replace(/[{}]+/g, "")] || "");
});
return JSON.parse(template_string);
}
/**
* Escape cell data to make JSON safe
* @see
* @param {string} str to escape JSON special characters from
* @return {string} escaped string
*/
function escapeData_(str) {
return str
.replace(/[\]/g, '\\')
.replace(/[\"]/g, '\\"')
.replace(/[\/]/g, '\/')
.replace(/[\b]/g, '\b')
.replace(/[\f]/g, '\f')
.replace(/[\n]/g, '\n')
.replace(/[\r]/g, '\r')
.replace(/[\t]/g, '\t');
};
}
虽然我是一个完全的菜鸟,但我无法在原始 createNewGoogleDocs
函数内的 foreach
循环中成功调用 fillInTemplateFromObject_
函数,这就是我的假设我应该怎么做?
对于由于缺乏经验而可能出现的选词不当之处,我们深表歉意,并在此先感谢大家的支持。
你的情况,下面的修改怎么样?
修改后的脚本:
请设置模板文档 ID 和文件夹 ID。
function createNewGoogleDocs() {
const documentLink_Col = "Document Link";
const template = DriveApp.getFileById('templateIdGoesHere');
const destinationFolder = DriveApp.getFolderById('destinationFolderIdGoesHere');
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('data');
const [header, ...values] = sheet.getDataRange().getDisplayValues();
const documentLink_ColIndex = header.indexOf(documentLink_Col);
const data = values.map(r => {
const temp = r.reduce((o, c, j) => {
o[header[j]] = c;
return o;
}, {});
return { filename: `${r[0]} ${r[1]} Report`, obj: temp };
});
const v = data.map(({ filename, obj }) => {
if (obj[documentLink_Col]) return [obj[documentLink_Col]];
const templateCopy = template.makeCopy(filename, destinationFolder); //create a copy of the template document
const templateCopyId = DocumentApp.openById(templateCopy.getId());
const templateCopyBody = templateCopyId.getBody();
Object.entries(obj).forEach(([k, v]) => templateCopyBody.replaceText(`{{${k}}}`, v));
templateCopyId.saveAndClose();
const url = templateCopyId.getUrl();
return [url];
});
sheet.getRange(2, documentLink_ColIndex + 1, v.length, 1).setValues(v);
}
在此修改中,创建了包含 header 和值的 object。使用这个object,动态使用占位符。并且,在创建文档后,将 URL 放入 Document Link
.
列中
例如,当你更改电子表格的header值和模板文档的占位符时,可以使用此脚本。
参考文献:
我有一个脚本,它用 Google Sheet 中每一行的数据替换 Google 文档模板中的每个 {{Keyword}}。 (下面的示例 sheet)
Name | Address | City | Document Link |
---|---|---|---|
Name 1 | Address 1 | City 1 | the script writes the new doc URL here |
Name 2 | Address 2 | City 2 | the script writes the new doc URL here |
这是我目前运行(成功)的代码:
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu('Documents & Mail Merge')
.addItem('Generate Documents', 'createNewGoogleDocs')
.addSeparator()
.addToUi();
}
function createNewGoogleDocs() {
const documentLink_Col = ("Document Link");
const template = DriveApp.getFileById('templateIdGoesHere');
const destinationFolder = DriveApp.getFolderById('destinationFolderIdGoesHere');
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('data');
const data = sheet.getDataRange().getDisplayValues();
const heads = data[0]; // Assumes row 1 contains the column headings
const documentLink_ColIndex = heads.indexOf(documentLink_Col);
data.forEach(function(row, index){
if(index === 0 || row[documentLink_ColIndex]) return;
const templateCopy = template.makeCopy(`${row[0]} ${row[1]} Report`, destinationFolder); //create a copy of the template document
const templateCopyId = DocumentApp.openById(templateCopy.getId());
const templateCopyBody = templateCopyId.getBody();
templateCopyBody.replaceText('{{Name}}', row[0]);
templateCopyBody.replaceText('{{Address}}', row[1]);
templateCopyBody.replaceText('{{City}}', row[2]);
templateCopyId.saveAndClose();
const url = templateCopyId.getUrl();
sheet.getRange(index +1 , documentLink_ColIndex + 1).setValue(url);
})
}
我想实现的功能/更改的功能:
将硬编码的 {{Placeholders}}(例如 templateCopyBody.replaceText('{{Name}}', row[0]);
)替换为另一种将任何列名视为模板文档中潜在 {{Placeholder}} 的方法。
所以基本上 我应该可以自由地编辑、添加、移动或删除 sheet 中的列,而不必再对它们进行硬编码,而只是调整模板。
可能有帮助,我发现了一种类似的脚本,它使用 Gmail 草稿作为模板而不是 Google 文档,根据我的理解,这里有 2 个函数可以实现我需要的功能:
/**
* Fill template string with data object
* @see
* @param {string} template string containing {{}} markers which are replaced with data
* @param {object} data object used to replace {{}} markers
* @return {object} message replaced with data
*/
function fillInTemplateFromObject_(template, data) {
// We have two templates one for plain text and the html body
// Stringifing the object means we can do a global replace
let template_string = JSON.stringify(template);
// Token replacement
template_string = template_string.replace(/{{[^{}]+}}/g, key => {
return escapeData_(data[key.replace(/[{}]+/g, "")] || "");
});
return JSON.parse(template_string);
}
/**
* Escape cell data to make JSON safe
* @see
* @param {string} str to escape JSON special characters from
* @return {string} escaped string
*/
function escapeData_(str) {
return str
.replace(/[\]/g, '\\')
.replace(/[\"]/g, '\\"')
.replace(/[\/]/g, '\/')
.replace(/[\b]/g, '\b')
.replace(/[\f]/g, '\f')
.replace(/[\n]/g, '\n')
.replace(/[\r]/g, '\r')
.replace(/[\t]/g, '\t');
};
}
虽然我是一个完全的菜鸟,但我无法在原始 createNewGoogleDocs
函数内的 foreach
循环中成功调用 fillInTemplateFromObject_
函数,这就是我的假设我应该怎么做?
对于由于缺乏经验而可能出现的选词不当之处,我们深表歉意,并在此先感谢大家的支持。
你的情况,下面的修改怎么样?
修改后的脚本:
请设置模板文档 ID 和文件夹 ID。
function createNewGoogleDocs() {
const documentLink_Col = "Document Link";
const template = DriveApp.getFileById('templateIdGoesHere');
const destinationFolder = DriveApp.getFolderById('destinationFolderIdGoesHere');
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('data');
const [header, ...values] = sheet.getDataRange().getDisplayValues();
const documentLink_ColIndex = header.indexOf(documentLink_Col);
const data = values.map(r => {
const temp = r.reduce((o, c, j) => {
o[header[j]] = c;
return o;
}, {});
return { filename: `${r[0]} ${r[1]} Report`, obj: temp };
});
const v = data.map(({ filename, obj }) => {
if (obj[documentLink_Col]) return [obj[documentLink_Col]];
const templateCopy = template.makeCopy(filename, destinationFolder); //create a copy of the template document
const templateCopyId = DocumentApp.openById(templateCopy.getId());
const templateCopyBody = templateCopyId.getBody();
Object.entries(obj).forEach(([k, v]) => templateCopyBody.replaceText(`{{${k}}}`, v));
templateCopyId.saveAndClose();
const url = templateCopyId.getUrl();
return [url];
});
sheet.getRange(2, documentLink_ColIndex + 1, v.length, 1).setValues(v);
}
在此修改中,创建了包含 header 和值的 object。使用这个object,动态使用占位符。并且,在创建文档后,将 URL 放入
列中Document Link
.例如,当你更改电子表格的header值和模板文档的占位符时,可以使用此脚本。