【问题标题】:Creating a Google Doc from a template with tables in Google Apps Script从包含 Google Apps 脚本中的表格的模板创建 Google Doc
【发布时间】:2022-02-02 22:41:10
【问题描述】:

这是一个 Google Apps 脚本,用于从模板和 Google 电子表格中的值创建 Google 文档。

它从您的 Google 电子表格中的活动行(具有突出显示的单元格或行的行)中获取字段,并使用 Google 文档模板(由 TEMPLATE_ID 标识)创建一个文档,其中这些字段替换模板中的占位符。占位符通过在它们周围有 % 来标识,例如%姓名%。它使用工作表中创建的自定义“创建文档”菜单运行。

现在,我的问题是,如果我还有一张带有普通占位符的表格(请参见图片或链接),如果我在文档模板中也放置了占位符 %Table 1% ,我该如何继续?

在这种情况下,占位符 %Name%、%Surname%、%Fruit% 必须仅从第二行获取值。占位符 %Table 1% 必须取值直到表的末尾(第 5 行)。

https://docs.google.com/spreadsheets/d/1l3bCuyRnz_ylMHMfhbRC-_L5bLkWwEkrzGKgy46Kvnk/edit?usp=sharing

function myfunction() {
      var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
    var TEMPLATE_ID = 'xxx';
    //var TEMPLATE_ID = ss.getRange("TEMPLATEID").getValue();
     var ui = SpreadsheetApp.getUi();
      if (TEMPLATE_ID === '') {
        
        SpreadsheetApp.getUi().alert('TEMPLATE_ID needs to be defined in code.gs')
        return
      }
      
      var copyFile = DriveApp.getFileById(TEMPLATE_ID).makeCopy(),
          copyId = copyFile.getId(),
          copyDoc = DocumentApp.openById(copyId)
           var docFile = DriveApp.getFileById(copyFile.getId()); // Get Document as File
      
          var FILE_NAME = ui.prompt('Insert file name:', ui.ButtonSet.OK);
          FILE_NAME.getSelectedButton() == ui.ButtonSet.OK
          copyDoc.setName(FILE_NAME.getResponseText())
          
      var copyBody = copyDoc.getActiveSection(),
          activeSheet = SpreadsheetApp.getActiveSheet(),
          numberOfColumns = activeSheet.getLastColumn(),
          activeRowIndex = activeSheet.getActiveRange().getRowIndex(),
          activeRow = activeSheet.getRange(activeRowIndex, 1, 1, numberOfColumns).getValues(),
          headerRow = activeSheet.getRange(1, 1, 1, numberOfColumns).getValues(),
          columnIndex = 0
        
      for (;columnIndex < headerRow[0].length; columnIndex++) {
          var nextValue = formatString(activeRow[0][columnIndex])
          copyBody.replaceText('%' + headerRow[0][columnIndex] + '%', nextValue)                         
        }
      
      copyDoc.saveAndClose()
      
      SpreadsheetApp.getUi().alert('File doc created!!')
      
    }

【问题讨论】:

  • 考虑到您通常只使用单行的数据,该表是单行,还是会占用这三列的所有数据行?
  • 简单地说,就像在这种情况下,占位符 %Name%、%Surname%、%Fruit% 必须仅从第二行获取值。占位符 %Table 1% 必须取值直到表的末尾(第 5 行)。

标签: google-apps-script google-sheets google-docs placeholder


【解决方案1】:

解决办法:

  • 定义哪些列对应于表数据。在下面的示例中(通过下面示例中的tableColumn 完成,其中第一个值对应于表的第一列,第二个值对应于最后一列)。
  • 遍历工作表列时,检查列是否对应于表值。如果没有,只需执行与原始代码相同的操作。如果是这样:
  • 获取与这些列相关的所有值 (tableValues)。
  • 使用findText(searchPattern) 获取存在`%Table 1% 占位符的元素。
  • 使用getChildIndex 获取该占位符所在元素的索引,并删除该元素的文本(占位符)。
  • 使用insertTable(childIndex, cells) 使用源值在指定索引处插入表。

代码示例:

function myfunction() {
  var tableColumn = [3,5]; // Table header is in column C to E
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var TEMPLATE_ID = 'xxx';
  var ui = SpreadsheetApp.getUi();
  if (TEMPLATE_ID === '') {        
    ui.alert('TEMPLATE_ID needs to be defined in code.gs')
    return
  }
  var copyFile = DriveApp.getFileById(TEMPLATE_ID).makeCopy();
  var copyId = copyFile.getId();
  var copyDoc = DocumentApp.openById(copyId);
  var FILE_NAME = ui.prompt('Insert file name:', ui.ButtonSet.OK);
  copyDoc.setName(FILE_NAME.getResponseText());
  var copyBody = copyDoc.getBody();
  var lastColumn = sheet.getLastColumn();
  var activeRowIndex = sheet.getActiveRange().getRowIndex();
  var activeRow = sheet.getRange(activeRowIndex, 1, 1, lastColumn).getValues()[0];
  var headerRow = sheet.getRange(1, 1, 1, lastColumn).getValues()[0];
  for (var columnIndex = 0; columnIndex < headerRow.length; columnIndex++) {
    if (columnIndex === tableColumn[0] - 1) { // Check if column corresponds to table data
      try {
        var tableValues = sheet.getRange(2, tableColumn[0], sheet.getLastRow()-1,tableColumn[1]-tableColumn[0]+1).getValues();
        var placeholder = `%${headerRow[tableColumn[0]-1]}%`;
        var rangeElement = copyBody.findText(placeholder);
        var element = rangeElement.getElement();
        console.log(element.asText().getText())
        var childIndex = copyBody.getChildIndex(element.getParent());
        console.log(childIndex)
        const elementText = copyBody.getChild(childIndex).asText().getText();
        const [beforeText, afterText] = elementText.split(placeholder);
        copyBody.getChild(childIndex).asText().setText('');
        copyBody.insertParagraph(childIndex, beforeText);
        copyBody.insertTable(childIndex+1, tableValues);
        copyBody.insertParagraph(childIndex+2, afterText);
      } catch(err) {
        continue;
      }
    } else if (columnIndex < tableColumn[0] - 1 || columnIndex > tableColumn[1] - 1) {
      var nextValue = formatString(activeRow[columnIndex]);
      copyBody.replaceText('%' + headerRow[columnIndex] + '%', nextValue);
    }                     
  }
  copyDoc.saveAndClose();
  SpreadsheetApp.getUi().alert('File doc created!!')     
}

【讨论】:

  • 你好。我已经创建了包含所有占位符(%Name%、%Surname%、%Fruit% %Table 1%)的模板,然后我尝试了你的脚本,但是当我在生成的文档中选择第 2 行后激活脚本时,我只找到了表格,没有找到其他字段(%Name%、%Surname%、%Fruit%)。为什么?
  • 另外,如果我在模板中有其他文字,你的脚本会删除所有内容...为什么? docs.google.com/document/d/…
  • @Gavis 出于某种原因,您共享的模板仅包含两个 elements(两段),第二段涵盖从 Name%Fruit% 的所有上下文。由于代码删除了所有以前的元素内容,它也删除了所有文本。也就是说,占位符应该在一个单独的元素中。
  • @Gavis 在任何情况下,我都会用另一种方法更新我的答案,通过检索它并创建 3 个新元素(表格之前、表格和表格之后)将其他文本保留在元素中。该解决方法会删除该元素中文本的先前格式,因此我真的建议修改源模板以确保表格占位符位于单独的元素中,不与其他文本共享。我希望这对你有用。
  • @lamblichus 如果我想添加 2 个表?我必须如何编辑你的代码?
猜你喜欢
  • 1970-01-01
  • 2015-12-30
  • 1970-01-01
  • 1970-01-01
  • 2014-05-18
  • 2019-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多