脚本概览

应用制作工具使您可以在不编写代码的情况下创建相对复杂的应用,但有时您需要使用微件的功能,而仅仅模型编辑器无法提供。应用制作工具的 JavaScript 脚本编辑器可以帮助您弥补这一不足。

应用制作工具中有两种脚本:在用户浏览器上运行的客户端脚本和在应用制作工具服务器上运行的服务器脚本。下面举例说明您可以使用应用制作工具中的脚本执行的操作:

  • 执行应用特定逻辑和计算
  • 连接到外部 Web 服务
  • 创建自定义权限检查
  • 等等…

客户端和服务器脚本

虽然客户端和服务器脚本都运行 JavaScript,但它们的使用场景和工作方式不同。

客户端脚本简介

  • 客户端脚本在用户的浏览器上运行。 它们可以访问 DOM,浏览器本地存储等。
  • 客户端脚本可以编辑微件属性。 应用制作工具将微件公开为 JavaScript 对象,因此您可以以编程方式更改其外观、事件和其他属性。
  • 客户端脚本屏蔽浏览器。 为了保持应用的及时响应,客户端脚本中应避免进行复杂的计算。

  • 客户端 API 是异步的。 某些客户端脚本的调用不按顺序执行。如需了解详情,请参阅下面的“使用数据模型”示例。

  • 客户端脚本不安全。 高级用户可以使用浏览器工具查看和更改客户端脚本。切勿依赖它们来保证数据安全或一致性。

服务器脚本简介

  • 服务器脚本在 Apps 脚本上运行。 它们可以直接访问数据库并使用日历、电子邮件等 Apps Script API
  • 服务器脚本不会保持请求之间的状态。 它们在分布式服务器上运行,因此每次调用脚本时,其全局变量均为空。
  • 服务器脚本有时会受到配额限制。 Apps 脚本服务的调用受每日配额限制。
  • 服务器脚本可以是安全的。 与客户端脚本不同,您可以信任服务器脚本来控制对敏感数据的访问。使用它们进行审核并强制实施数据一致性。

脚本示例

要了解客户端和服务器脚本的不同,最简单的方法是通过示例。以下脚本来自一个待办事项列表应用,该应用允许用户创建多个待办事项列表,每个待办事项列表都包含多个待办事项。

客户端:与页面和微件交互

当用户点击 DeleteTodoListButton 时,该按钮的 onClick 脚本切换为确认页面:

    app.showPage(app.pages.DeleteTodoListConfirmationView);
    

客户端:实现属性绑定

该脚本是 DeleteTodoListButtonenabled 属性的绑定表达式。它确保仅当列表中没有任何待办事项时才启用该按钮,因此用户只能删除空的待办事项列表。

    @datasource.item != null && @datasource.item.TodoItems.length == 0
    

客户端和服务器:使用数据模型

客户端:

该脚本会创建并选择新的待办事项列表。它首先调用服务器脚本来创建新的列表记录,然后重新加载 TodoList 模型,最后选择新创建的列表。

    function onCreateTodoListClick() {
      var datasource = app.datasources.TodoList;
      google.script.run.withSuccessHandler(function(newKey) {

        // When created, redisplay todo lists.
        datasource.load(function() {

          // When reloaded, select new todo list.
          datasource.selectKey(newKey);
        });
      }).createTodoListWithItem();
    }
    

您可能会注意到代码与上面的操作顺序不符。这是因为该脚本和许多客户端脚本一样,是异步执行的。该脚本不能仅仅是先执行 createTodoListWithItem(),然后再执行 load(),因为两者异步,不能保证重新加载数据源之前会创建新的待办事项列表。

相反,借助成功创建待办事项列表项后才会执行的回调,createTodoListWithItem() 可以调用 load()。如需详细了解异步脚本和回调,请参阅客户端脚本

服务器:

该服务器脚本是 createTodoListWithItem(),从上面的客户端脚本调用。它访问应用的数据库以创建新的待办事项列表和该列表中的事项,然后返回列表的 Key

    function createTodoListWithItem() {
      // Automatically number todo lists for the user.
      var lock = LockService.getScriptLock();
      lock.waitLock(10000);
      var query = app.models.TodoList.newQuery();
      var allTodoLists = query.run();
      var todoList = app.models.TodoList.newRecord();
      var newListNumber = allTodoLists.length + 1;
      todoList.name = "My list " + newListNumber;
      app.saveRecords([todoList]);
      lock.releaseLock();

      // Create first todo item for user convenience.
      var todoItem = app.models.TodoItem.newRecord();
      todoItem.TodoList = todoList;
      app.saveRecords([todoItem]);
      return todoList._key;
    }
    

服务器脚本不会自动保存数据,因此该脚本调用 saveRecords() 来保存对模型的更改。编写自己的脚本来创建或编辑记录时,请记住这样做。

该脚本还使用锁定来确保数据一致性。如需详细了解锁定,请参阅服务器脚本

服务器:安全、审核和数据一致性

服务器脚本可帮助您保持数据的一致性和安全。上面的脚本使用锁定服务来实现查询的原子性并创建操作。

您还可以通过使用异常来禁止某些模型事件,从而实现自定义权限。举例来说,将以下内容添加到模型的 onDelete() 事件可防止删除记录:

    throw "Deletions now allowed";
    

您还可以使用 onCreate() 处理程序创建审核:

    var email = Session.getActiveUser().getEmail();
    record.setAudit("Created on " + new Date() + " by user " + email + "\n");
    

客户端和服务器:从其他网址提取数据

客户端:

使用 XMLHttpRequest 或应用制作工具的 HtmlArea 微件注入 IFRAME

服务器:

使用 Apps 脚本的网址提取服务

客户端:集成自定义 JavaScript 库

目前有以下两种方法可以加载外部 JavaScript 库,具体取决于库:

JSHint 用于脚本验证

应用制作工具支持某些 JSHint 指令。举例来说,您可以使用 ignore 指令关闭代码块的静态检查工具:

/* jshint unused: true, eqnull: false */
    var w = 3;   // Warning: w is defined but never used.
    alert(10 == null); // Warning: use '===' to compare with 'null'.

    /* jshint ignore:start */
    var y = 4;     // No warning.
    alert(10 == null); //  No warning.
    /* jshint ignore:end */

    var a = 5;   // Warning: a is defined but never used.
    alert(10 == null); // Warning: use '===' to compare with 'null'.
    

如需详细了解 JSHint,请参阅 JSHint 文档