【问题标题】:GAS Data from server side function not seen in successHandler function in HTML在 HTML 中的 successHandler 函数中看不到来自服务器端函数的 GAS 数据
【发布时间】:2021-03-14 15:16:43
【问题描述】:

我有一个装订本的 Apps 脚本。我在日志中看到了我的服务器端代码(listCourses),找到了大量数据,最后一条语句是返回数据,但客户端中的 JSON.stringify withSuccessHandler 代码(afterServerRepl(e))显示为空?

以下代码来自 Alan Wells 的回答。我将其命名为“tinyTestHtml.html”。

<!DOCTYPE html>
<html>
  <head>
    <!--  tinyTestHtml.html -->
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Choose a class</title>
    <base target="_top">
    <?!= HtmlService.createHtmlOutputFromFile('cssStyle').getContent(); ?>
  </head>
  <body>
    This is the body


    <script>
      window.onload = function() {
        console.log('hah! it ran. ');
        google.script.run
          .withSuccessHandler(afterServerRepl)
          .listCourses();
       };

       function afterServerRepl(e) {
         console.log('Server reply was successful!');
         console.log(JSON.stringify(e) );
       };

//       window.afterServerRepl = function(e) {
//         console.log('Server reply was successful!');
//         console.log(JSON.stringify(e) );
//       };
    </script>
  </body>
</html>

此 HTML 从绑定到电子表格的自定义菜单中显示。以下是一个 fakeListCourses,因为只有在 Google Classroom 中有课程,您才能获得输出。真正的代码包含在底部。您可以看到结果与名称和 id 的二维数组相同。

function listCourses() {
  console.log('Begin listCourses');

  let unsrtCrsList = [
    ['Essential Ed', '277964684794'],
    ['TestEnglish', '256514192951'],
    ['D  Class', '57299927845'],
    ['AEL/ESL Classs', '250327591120'],
    ['Day Time ESL Multi-Level Classs', '171258050042'],
    ['English Levels 4/5s', '119335686849']
    ];
  console.log(JSON.stringify(unsrtCrsList));
  /*
    [["Essential Ed","277964684794"],["TestEnglish","256514192951"],["D  Class","57299927845"],["AEL/ESL Classs","250327591120"],["Day Time ESL Multi-Level Classs","171258050042"],["English Levels 4/5s","119335686849"]]
    */
  return unsrtCrsList;
}

这是创建侧边栏的函数,它是一个模板,确实使用了 EVALUATE。

function showTinyTestHtml() {
  console.log('Begin showTinyTestHtml');
  const ui = SpreadsheetApp.getUi();
  
  /**
   * Opens a sidebar to allow person to a single class
   */
  let template = HtmlService
      .createTemplateFromFile('tinyTestHtml');

  let htmlOutput = template.evaluate()
      .setTitle('Choose a Class')
      .setWidth(400);

  SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
      .showSidebar(htmlOutput);
  
}

这是文件“cssStyle.html

    <style>
      /* comment */
      h1 {
        font-size: 20px;
      }
      
      h2 {
        font-size: 18px;
        font-weight: bold;
        padding: 5px; 
      }
      
      h3 {
        font-size: 16px;
        font-weight: bold;
        padding: 3px; 
      } /*
      .button {
        font: 1em serif;
        width: 50px;
      }

      button {
        background:  #ffffff;
        border:      2px solid #333333;
        border-radius: 5px;
        cursor:      pointer;
        font:        1em serif;
        font-size:   20;
        margin-left: 40px;
        width:       70px;
      }

      button:hover,
      button:focus {
        outline     : none;
        background  : #000000;
        color       : #FFFFFF;
      }
*/      
      #errMsg {
        border:  #ffffff;
        color:   red;
        font-size: 14px;
      }
      
      form  {
        font-size: 14px;
        margin: 0;  /* Center the form on the page */
        width: 450px;
      }
      
      form li + li {
        margin-top: 3px; 
      }
      
      input  {
        border: 1px solid #ffffff;
        box-sizing: border-box;
        font: 1em serif;
        width: 250px; 
      }
      
      .input {
        border: 1px solid #ffffff;
        box-sizing: border-box;
      }
      
      label {
        display: inline-block;
        font-weight: bold;
        text-align: right;
        width: 100px; 
      }
      
      p    {
        margin: 3px auto;
        padding: 3px; 
      }
      
      .parent  {
        background : #eeeeee;
        border: solid 2px;
        padding: 10px; 
      }
      
      ul {
        list-style: none;
        padding: 0;
        margin: 0;
      }
    </style>

控制台窗口

来自真实 listCourses 的 Cocde。仅当您在 Google 课堂中有课程时,这才会为您提供数据。我已经包含了它得到的显示,所以你可以看到它与我上面的假 listCourses 相同。

/**
 * https://developers.google.com/classroom/quickstart/apps-script
 */
function listCourses() {
  console.log('Begin listCourses');
  const optionalArgs = {
    pageSize: 20
  };
  const response = Classroom.Courses.list(optionalArgs);
  const courses = response.courses;
  let unsrtCrsList = [];
  if ( courses               && 
       courses.length > 0    )    {
    for ( i = 0 ; i < courses.length ; i++ )    {
      let crsObj = courses[i];
      if ( courses[i].courseState === 'ACTIVE' )  {
        cName = courses[i].name;
        cId = courses[i].id;
        unsrtCrsList.push([cName, cId]);
      }
    }
    //  sort by first value ascending
    let srtCrsList =  gasArraySort(unsrtCrsList, 0, -1 );
    console.log(JSON.stringify(srtCrsList) );
    console.log('End listCourses - # course: ', srtCrsList.length );
    return srtCrsList;
  } else {
    console.log('End listCourses - no courses found.');
    return;
  }
}

/*
Begin listCourses
[["Essential Ed w Mattie","277964684794"],["LissaTestEnglish","256514192951"],["Lourdes AEL/ESL Class","250327591120"],["Lourdes Day Time ESL Multi-Level Class","171258050042"],["Lourdes's ESL Class","130831138700"],["Ms. Dee's Class","57299927845"],["2001-04-05T06:00:00.000Z","119335686849"]]
End listCourses - # course:  9
*/

这可以替代我在Tanaike File Explorer instead of Picker getFiles has data but not seen in importData 提出的困惑问题

请不要使用 AJAX、JQUERY 和其他花哨的东西。只是普通的 Google Apps 脚本。我只是一个初学者。

            • 添加数组排序我正在使用 3/25

我已将问题缩小到 getClasses / getFiles 逻辑,可能是我尝试对返回列表中的文件进行排序。这是我正在使用的数组排序例程。

/**
 *   https://sites.google.com/site/nnillixxsource/Notable/PivotChartsLib/ArrayLib
 *   sort ( data, columnIndex, ascOrDesc )
 *   veröffentlicht um 03.10.2013, 05:49 von nnil lixx   
 *   [ aktualisiert: 07.12.2013, 17:55 ]
 *   ArrayLib.sort = 
 *   I do not know how to hook up to this library
 */
function gasArraySort(data, columnIndex, ascOrDesc) {
    if (data.length > 0) {
        if (typeof columnIndex != "number" || columnIndex > data[0].length) {
            throw "Choose a valide column index";
        }
        var r = new Array();
        var areDates = true;
        for (var i = 0; i < data.length; i++) {
            if (data[i] != null) {
                var date = new Date(data[i][columnIndex]);
                if (isNaN(date.getYear()) && data[i][columnIndex] != "") {
                    areDates = false;
                } else {
                    if (data[i][columnIndex] != "") {
                        data[i][columnIndex] = date;
                    }
                }
                r.push(data[i]);
            }
        }
        return r.sort(function (a, b) {
            if (ascOrDesc) {
                return ((a[columnIndex] < b[columnIndex]) ? -1 : ((a[columnIndex] > b[columnIndex]) ? 1 : 0));
            }
            return ((a[columnIndex] > b[columnIndex]) ? -1 : ((a[columnIndex] < b[columnIndex]) ? 1 : 0));
        });
    } else {
        return data;

我根据我的实际 listClasses 函数的 JSON.stringify 构建了假列表类。我比较了排序后的代码和原始代码的输出,除了排序顺序之外看不到其他差异。但是,当我在以下两个版本的 getFiles 之间切换时,带有排序的版本在 successHandler 中显示为 null。

这是任何人都可以运行的代码(不需要类)。使用 inMimeType = 'EVERYTHING' 获得与原始结果相同的结果。新代码对文件进行排序。原始代码没有。原始代码和新代码找到相同数量的文件,但新代码数据对进程开始的 HTML 中的 successHandler 不可见。

function getFiles(e, rootFolderId, inMimeType) {
  console.log('- - - - - - - - - Begin getFiles inMimeType: ', inMimeType );
  console.log(JSON.stringify(e) );

  var data = {};
  var idn = e;

  e = e == "root" ? DriveApp.getRootFolder().getId() : e;
  data[e] = {};
  data[e].keyname = DriveApp.getFolderById(e).getName();
  data[e].keyparent = idn == rootFolderId
    ? null : DriveApp.getFolderById(e).getParents().hasNext()
    ? DriveApp.getFolderById(e).getParents().next().getId() : null;
  data[e].files = [];
  var da = idn == "root" ? DriveApp.getRootFolder() : DriveApp.getFolderById(e);
  var folders = da.getFolders();
  var files = da.getFiles();
  
  while (folders.hasNext()) {
    var folder = folders.next();
    data[e].files.push({name: folder.getName(), id: folder.getId(), mimeType: "folder"});
  }

// new code
  var fileArr = [];
  while (files.hasNext()) {
    var file = files.next();
    console.log('6 file.getMimeType(): ', file.getMimeType());
//    data[e].files.push({name: file.getName(), id: file.getId(), mimeType: file.getMimeType()});
    if ( file.getMimeType() === 'folder' )    {;
      // do not save any files
    }    else if ( file.getMimeType() === inMimeType )    {
      console.log('7 push fileArr name: ', file.getName() );
      fileArr.push([file.getName(),file.getId(), file.getMimeType()]);
    } else if ( inMimeType === 'EVERYTHING' )  {
      console.log('8 push fileArr name: ', file.getName() );
      fileArr.push([file.getName(),file.getId(), file.getMimeType()]);
    } 
  }  
  console.log('     # files after files.hasNext: ', data[e].files.length );
  
  let sorted = gasArraySort(fileArr, 0, true);
  let numFiles = sorted.length;
  console.log('# after sort: ;', numFiles );
  for ( i = 0 ; i < numFiles ; i++ )  {
    console.log('pushing sorted[i][0]: ', sorted[i][0] );
    data[e].files.push({name: sorted[i][0], id: sorted[i][1], mimeType: sorted[i][2]});
  }

// original code
  // while (files.hasNext()) {
  //   var file = files.next();
  //   data[e].files.push({name: file.getName(), id: file.getId(), mimeType: file.getMimeType()});
  // }
  
  console.log('     # files after both while loops: ', data[e].files.length );
  console.log('end getFiles - ', JSON.stringify(data) );
  return data;
}

这里是等价的好坏listClasses代码和日志

启用好的代码

function listCourses() {
  console.log('Begin listCourses');
  const optionalArgs = {
    pageSize: 20
  };
  const response = Classroom.Courses.list(optionalArgs);
  const courses = response.courses;
  
  let unsrtCrsList = [], srtCrsList = [];
  console.log('# courses: ', courses.length );
  //code without sort
  if ( courses               && 
      courses.length > 0    )    {
    for ( i = 0 ; i < courses.length ; i++ )    {
      let crsObj = courses[i];
      if ( courses[i].courseState === 'ACTIVE' )  {
        cName = courses[i].name;
        cId = courses[i].id;
        console.log('pushing name: ', cName );
        srtCrsList.push([cName, cId]);
      }
    }
    console.log(JSON.stringify(srtCrsList) );
    return srtCrsList;
  
  // code with sort
//  if ( courses               && 
//       courses.length > 0    )    {
//    for ( i = 0 ; i < courses.length ; i++ )    {
//      let crsObj = courses[i];
//      if ( courses[i].courseState === 'ACTIVE' )  {
//        cName = courses[i].name;
//        cId = courses[i].id;
//        unsrtCrsList.push([cName, cId]);
//      }
//    }
//    //  sort by first value ascending
//    srtCrsList =  gasArraySort(unsrtCrsList, 0, -1 );
//    console.log(JSON.stringify(srtCrsList) );
//    console.log('End listCourses - # course: ', srtCrsList.length );
//    return srtCrsList;

  } else {
    console.log(JSON.stringify(srtCrsList) );
    console.log('End listCourses - no courses found.');
    return srtCrsList;
  } 
}

良好的代码日志条目

Mar 25, 2021, 3:47:08 PM    Debug   Begin listCourses
Mar 25, 2021, 3:47:08 PM    Debug   # courses:  9
Mar 25, 2021, 3:47:08 PM    Debug   pushing name:  Essential Ed w Mattie
Mar 25, 2021, 3:47:08 PM    Debug   pushing name:  LissaTestEnglish
Mar 25, 2021, 3:47:08 PM    Debug   pushing name:  Ms. Dee's Class
Mar 25, 2021, 3:47:08 PM    Debug   pushing name:  Lourdes AEL/ESL Class
Mar 25, 2021, 3:47:08 PM    Debug   pushing name:  Lourdes Day Time ESL Multi-Level Class
Mar 25, 2021, 3:47:08 PM    Debug   pushing name:  Lourdes's ESL Class
Mar 25, 2021, 3:47:08 PM    Debug   pushing name:  English with Lissa Levels 4/5
Mar 25, 2021, 3:47:08 PM    Debug   [["Essential Ed w Mattie","277964684794"],["LissaTestEnglish","256514192951"],["Ms. Dee's Class","57299927845"],["Lourdes AEL/ESL Class","250327591120"],["Lourdes Day Time ESL Multi-Level Class","171258050042"],["Lourdes's ESL Class","130831138700"],["English with Lissa Levels 4/5","119335686849"]]

启用了错误代码

/**
 * https://developers.google.com/classroom/quickstart/apps-script
 */
function listCourses() {
  console.log('Begin listCourses');
  const optionalArgs = {
    pageSize: 20
  };
  const response = Classroom.Courses.list(optionalArgs);
  const courses = response.courses;
  
  let unsrtCrsList = [], srtCrsList = [];
  console.log('# courses: ', courses.length );
  //code without sort
//  if ( courses               && 
//      courses.length > 0    )    {
//    for ( i = 0 ; i < courses.length ; i++ )    {
//      let crsObj = courses[i];
//      if ( courses[i].courseState === 'ACTIVE' )  {
//        cName = courses[i].name;
//        cId = courses[i].id;
//        console.log('pushing name: ', cName );
//        srtCrsList.push([cName, cId]);
//      }
//    }
//    console.log(JSON.stringify(srtCrsList) );
//    return srtCrsList;
  
  // code with sort
  if ( courses               && 
       courses.length > 0    )    {
    for ( i = 0 ; i < courses.length ; i++ )    {
      let crsObj = courses[i];
      if ( courses[i].courseState === 'ACTIVE' )  {
        cName = courses[i].name;
        cId = courses[i].id;
        unsrtCrsList.push([cName, cId]);
      }
    }
    //  sort by first value ascending
    srtCrsList =  gasArraySort(unsrtCrsList, 0, -1 );
    console.log(JSON.stringify(srtCrsList) );
    console.log('End listCourses - # course: ', srtCrsList.length );
    return srtCrsList;
  } else {
    console.log(JSON.stringify(srtCrsList) );
    console.log('End listCourses - no courses found.');
    return srtCrsList;
  }
}

记录错误条目

Mar 25, 2021, 3:42:32 PM    Debug   Begin listCourses
Mar 25, 2021, 3:42:32 PM    Debug   # courses:  9
Mar 25, 2021, 3:42:32 PM    Debug   [["Essential Ed w Mattie","277964684794"],["LissaTestEnglish","256514192951"],["Lourdes AEL/ESL Class","250327591120"],["Lourdes Day Time ESL Multi-Level Class","171258050042"],["Lourdes's ESL Class","130831138700"],["Ms. Dee's Class","57299927845"],["2001-04-05T06:00:00.000Z","119335686849"]]
Mar 25, 2021, 3:42:32 PM    Debug   End listCourses - # course:  7
            • 结束 3/25 更新

【问题讨论】:

  • 请提供 listcourses() 命令的示例,以便我们产生错误。请注意,这是要求 OP 提供 minimal reproducible example 的基础
  • 我不认为这个命令&lt;?!= HtmlService.createHtmlOutputFromFile('cssStyle').getContent(); ?&gt; 会起作用我认为它需要evaluate() 方法。但不可否认,我不太使用模板化的 html,所以我可能是错的。
  • 添加了 listCourses 的模拟(删除不相关的细节)并显示 HTML 代码。如果您想要我可以提供的真正 listCourses 但 JSON.stringify 具有相同的结构 [['name', 'id'],[...]]
  • 请添加minimal reproducible example'cssStyle0' 文件丢失)还以文本形式添加执行页面和网络浏览器控制台中显示的任何错误消息。
  • 添加了css和控制台窗口显示

标签: google-apps-script client-server google-classroom


【解决方案1】:

如果我使用您的代码部署 Web 应用程序,如下所示:

function doGet() {
  console.log('Begin showTinyTestHtml');
  /**
 * Opens a sidebar to allow person to a single class
 */
  let template = HtmlService
    .createTemplateFromFile('tinyTestHtml');

  let htmlOutput = template.evaluate()
    .setTitle('Choose a Class')
    .setWidth(400);

  return htmlOutput;
}

当我访问 Web 应用程序 URL 时,我阅读了控制台,并看到了 JSON 字符串,如下图所示:

更新:

如果我“按原样”使用您的脚本并从编辑器运行函数showTinyTestHtml,我也会得到预期的结果。 见

【讨论】:

  • 我接受这个作为答案,因为鉴于我分享的信息是真实的。后来我发现我在创建进入successHandler 的数据时出错了。我很惊讶数据并没有作为格式错误的混乱进入,而是被认为是空的,但是一旦正确格式化,一切都很好,正如你所证明的那样。非常感谢您花这么多时间来帮助我。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-11-24
  • 1970-01-01
  • 2020-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多